A. I have count data over a period of four years of fish from conducted count surveys at a landing site. The number of survey days each month is variable and some months have zero surveys. To compare years I have used: total number of fish/total survey days for each year. The data indicate a year to year decline.

B. To check for seasonal patterns I used the same method. Remember, some months were not surveyed. So I used the total number of fish/total survey days in a particular month pooled for all four years. Some months thus have count data from four years, some only from three or two. The data suggest a seasonal pattern with lower counts/day in the summer.

In both cases, how do I test for statistical significance? How do I incorporate the variability and significance of the number of survey days?

Thank you!

  • $\begingroup$ Welcome to cv, Tamas - this is an interesting question that should be answerable :-) Could you tell us: what is the range of daily counts, and how strong is the seasonal and yearly variation, roughly? (e.g. "1000-5000 fish/day, monthly day averages varied by 1000 (winter-summer), years by 1200") - just to get a feeling for the order of magnitude ? $\endgroup$
    – Ute
    Jul 19, 2023 at 15:39
  • 1
    $\begingroup$ Year 1: 27 counts/survey day. Year 2: 17/day. Year 3: 23/day. Year 4: 19/day. Year 5: 12/day. I should add that Year 1 and 5 are only half a year (second half) and year 5 is only first half. So I should probably not assume that I can compare those with the other "full" years... When looking at pooled monthly data the range for one of the species is 25/day in january to 4/day in june, going back up to 18 in december. $\endgroup$
    – Tamas
    Jul 19, 2023 at 16:28
  • $\begingroup$ Thanks! That gives a good idea about your data. Do you use R? $\endgroup$
    – Ute
    Jul 19, 2023 at 16:36
  • $\begingroup$ Yes I use R for the analysis. $\endgroup$
    – Tamas
    Jul 20, 2023 at 8:12
  • 1
    $\begingroup$ :-) OK, then google for "two way ANOVA in R" and work through a few examples (using lm for norma distributed data), trying to understand theory, what the outcome means and how it is affected when you play with the model. That should give you a sound basis. $\endgroup$
    – Ute
    Jul 20, 2023 at 14:47

1 Answer 1


Your data appear to be a perfect application for a generalized linear model (glm) : your variable of interests are counts that depend explanatory variables (month and year). I would recommend to try a Poisson regression model first - it may be good enough and at least you get a feeling for such models.

One of the advantages of Poisson regression models:
$ \quad $they automatically take care of days where no data were collected!

(see in the last section why months in one year with zero days are not a big issue)

There is a vast literature on glm, and here on cv there are many experienced users who can advise on even more sophisticated models such as general additive models (gam).

Poisson Regression Model for Fish Counts

The Poisson distribution is the to go for model for counts per unit - this could be the number of rabbits on an acre of land, or the number of phone calls you get during lunch time. The volume of that unit may vary.

The only parameter of the Poisson distribution is the mean number of counts $\lambda$. When you have units of varying volume or length $t$, it makes sense to write the mean as $\lambda = r t$, where $r$ is the rate.
In your case, when you analyse monthly data, $t$ is the number of survey days in a month, and $r$ is the average number of fish per day.

The rate depends on season or month, and year. It would probably make sense to expect that, if in year 1 you had on average 27 counts/day and in year 5 only 12, then you would see a similar decline when you compare the month march, say, in both years. So your independent variables month and year have a multiplicative effect on the rate.

Statistical model

Formally, let

  • $Y_1, Y_2, \dots Y_n$ be the counts that you have,
  • $t_1,\dots,t_n$ the number of survey days where these counts were taken,
  • $mn(1), \dots,mn(n)$ the month (january to december) and
  • $yr(1), \dots,yr(n)$ the year.

Then the $i$th count $Y_i$ is assumed to have a Poisson distribution with mean $\lambda_i = r_i\cdot t_i$, where $$ r_i = r_0\cdot \text{effect of month $mn(i)$} \ \cdot \ \text{effect of year $yr(i)$}, $$ $r_0$ is the baseline rate.
A model like this would be "overparametrized" - it would allow for infinitely many equivalent solutions for the best fit. Therefore, one sets restrictions to the parameter set. There are several options to do that. For example, R would set $\text{effect of month}$ to 0 for one month, and $\text{effect of year}$ to 0 for one of the years by default.

By taking the logarithm, this multiplicative model becomes an additive model. We would denote the log transformed rate by $\eta_i$, the log transformed baseline rate as $\beta_0$ and the log transformed effects as $\beta^{\text{month}}$ and $\beta^{\text{year}}$: $$ \eta_i = \log(\lambda_i) = \log(r_i\cdot t_i) = \log(t_i) + \beta_0 + \beta^{\text{month}}_{mn(i)} + \beta^{\text{year}}_{yr(i)}. $$

This looks like an ANOVA model for the log transformed mean $\eta_i$. The logarithm links $\eta_i$ with the rate $r_i$ that interests us. The model is called Poisson GLM with log link.

The term $log(t_i)$ is commonly called the offset.

Analysis and interpretation

Generalized linear models are fitted by maximum likelihood, such as known from the normal linear model (here maximum likelihood estimation is achieved by the least squares method). The maximum likelihood estimator for GLM is implemented in all major statistical software; in R as function glm.

After model fitting, residuals can be calculated, and goodness of fit can be assessed. Tests and confidence intervals for the model parameters, in your case $\beta_0, \beta^{\text{month}}, \beta^{\text{year}}$, are available, as known from the normal linear model.

You can directly translate from the fitted parameters back to "real world" by applying the inverse of the link function. Here, with the log link, you would apply the exponential function, and get estimates $$ \begin{aligned} \hat{r_i} &=\exp(\hat\beta_0 + \hat\beta^{\text{month}}_{mn(i)}+ \hat\beta^{\text{year}}_{yr(i)}) \\&= \exp(\hat\beta_0)\cdot \exp(\hat\beta^{\text{month}}_{mn(i)}) \cdot \exp(\hat\beta^{\text{year}}_{yr(i)}) \\ &= \hat r_0\cdot \widehat{\text{effect of month $mn(i)$}} \ \cdot \ \widehat{\text{effect of year $yr(i)$}}, \end{aligned} $$ and you can even calculate confidence intervals for the effects of month and year.

Where to start and where to go

Poisson regression is a good starting point to get into analysing your data with a suitable statistical model.

If you have never worked with glms before, try to familiarize yourself with the basic workflow of data analysis and interpretation. Analyzing simulated data can help to get a feeling for the method. Then start with a model where you have both year and month as categorical variables to see if you can see any effect.

With catch data like yours, it is not uncommon that there is considerable variation from day to day, more than you would expect for Poisson distributed counts. If you detect this in your data, you may want to move on to glms for overdispersed count data, such as the Negative Binomial Regression model.

Finally, there are possibilities to fit a smooth seasonal model without discretizing time into months, if you have daily data available, or to analyze the data like a time series, see for example this post on cv. This can be done with Generalized Additive Models (GAM), a further generalization of glm.


Why do I not have to worry about months with no counts?

Months with no counts have no information to contribute. When you analyze the data, you need to omit them, because you would otherwise create an offset of $\log(t_i)=\log(0)=-\infty$. Ideally, for best results, you would have the same number of survey days in each month, and thus a "balanced design" - this would give same precision for all effect sizes. Find more about the consequences of an imbalanced design in this thread.

How to "glm" the model in R

Assume, you have a dataframe fish with variables

  • counts: all monthly counts (individual for each month where counts were taken),
  • days : number of survey days,
  • month : name or number of month (factor variable),
  • year : factor variable with year,

Then you can fit the glm by

# make sure to exclude months with no survey days:
# --- in base R ---
fish <- fish[fish$days > 0, ]

# --- or with tidyverse ---
fish <- fish |> filter(days > 0)

# the glm call is then:

glm(counts ~ month + year , offset(log(days)),
    data = fish, family = poisson)
  • 1
    $\begingroup$ OMG. Thank you fir this! I was looking for a starting point and you gave me one for sure! $\endgroup$
    – Tamas
    Jul 20, 2023 at 8:21
  • $\begingroup$ How does this handle the "some months have zero surveys" issue since $\ln(0)$ is undefined? $\endgroup$
    – dimitriy
    Jul 26, 2023 at 17:40
  • $\begingroup$ Thanks for that comment, @dimitriy! I added a paragraph in the end, extended the code and found a typo in the code on that occasion :-) $\endgroup$
    – Ute
    Jul 26, 2023 at 18:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.