R - Panel data FE, no unique time-id match, creating new time-variable

Viewed 205

I am working with a highly disaggregated unbalanced panel data set in the long format on vehcile sales and want to run a FE regression model. The data structure is given like that (of course containing further information, but this is irrelevant for this case):

cars <- data.frame(make = c("Audi", "Audi", "Audi", "Audi", "Audi", "Audi", "Audi", "Audi", "Audi", "Audi", "Opel", "Opel", "Opel", "Opel"),
               model = c("a1", "a1", "a1", "a1", "a1", "a1", "a3", "a3", "a3", "a3", "Corsa", "Corsa", "Corsa", "Corsa"),
               trim = c("Sport", "Business", "Sport", "Business","Sport", "Business", "Cross", "Street", "Corss", "Street", "O1", "O2", "O1", "O2"),
               tax = c(100, 200, 100, 200, 100, 200, 500, 600,500, 600, 50, 30, 50, 30),
               sales = c(1000, 1500, 800, 1300, 1100, 1000, 50, 70, 30, 20, 5000, 2000, 3000, 3000),
               time = c(1, 1, 2, 2, 3, 3, 1, 1, 2, 2, 4, 4, 5, 5))

I hope you get the idea.

So basically I have panel data with trim and time being the index variables. I do want to examine the impact of the tax on sales. For that i want to run a FE regression with model and time because I only want to consider the variation in sales and tax within a car model and a time period. The FE capture the remaining variation, in which I am not interested. In order to do so I wanted to employ

plm(sales ~ tax, 
         data = cars,
         model = "within",
         index = c("time", "model"),
         effect = "twoways")

But this does not work because of the data structure with a narrower definition of vehcile (according to the trim variable). Thus, I have multiple rows with the same model for a single period (no unique id-time match). In order to overcome this issue I thought about creating a new time variable, because I dont not want to aggregate my data on trim-level on model-level. I am lacking a bit of imagination about the requirements for the new time variable and how to create it. This would optimally result in

plm(sales ~ tax, 
         data = cars,
         model = "within",
         index = c("new time variable", "model"),
         effect = "twoways")

But I also wonder if i could easily solve my problem with

lm(sales ~ tax + factor(time) + factor(model), data = cars)

Does anybody have a suggestion, either on my simple idea to overcome this problem or on the idea of creating a new time variable to run the plm command (or even a completely new idea or another package)? Moreover, would it make sense to run a FE regression with time-model FE as well as trim-FE?

2 Answers

please add a little reproducible dataset and try to be more clear with what you want.

I'm trying to answer anyways, Fixed Effects model is correct if your variables vary over time, if you have time-invariant variables might prefer a Pooled OLS or Random one (Please check here: Econometrics Academy).

Plm package is the correct one, however, for doing the regression I believe there is an error, below my suggestion:

    library(plm) 
    p.data <- pdata.frame(data, index=c("trim","time")) 
    attach(p.data) 
    y <- cbind(sales) 
    X <- cbind(lt_tax)          
    
    model1 <- plm(y~X+factor(time), data=p.data, model = "within", effect = "twoways")
    summary(model1)

Hope this is useful.

Since you asked to show how a new individual index can be contructed from model and trim, here is how to do that. However, not that your variable tax does not vary per model-trim combination (can check via, e.g., pvar or by looking at the model matrix after within transformation). Thus, your within model with model-trim being the individual index is non-estimable.

cars <- data.frame(make = c("Audi", "Audi", "Audi", "Audi", "Audi", "Audi", "Audi", "Audi", "Audi", "Audi", "Opel", "Opel", "Opel", "Opel"),
                   model = c("a1", "a1", "a1", "a1", "a1", "a1", "a3", "a3", "a3", "a3", "Corsa", "Corsa", "Corsa", "Corsa"),
                   trim = c("Sport", "Business", "Sport", "Business","Sport", "Business", "Cross", "Street", "Corss", "Street", "O1", "O2", "O1", "O2"),
                   tax = c(100, 200, 100, 200, 100, 200, 500, 600,500, 600, 50, 30, 50, 30),
                   sales = c(1000, 1500, 800, 1300, 1100, 1000, 50, 70, 30, 20, 5000, 2000, 3000, 3000),
                   time = c(1, 1, 2, 2, 3, 3, 1, 1, 2, 2, 4, 4, 5, 5))


# See NA coefficient in two-way LSDV model
summary(lm(sales ~ tax + factor(time) + factor(model), data = cars))
#> 
#> Call:
#> lm(formula = sales ~ tax + factor(time) + factor(model), data = cars)
#> 
#> Residuals:
#>     Min      1Q  Median      3Q     Max 
#> -1470.3  -135.1     0.0   131.3  1470.3 
#> 
#> Coefficients: (1 not defined because of singularities)
#>                     Estimate Std. Error t value Pr(>|t|)  
#> (Intercept)          762.884    904.730   0.843   0.4270  
#> tax                    2.972      5.056   0.588   0.5750  
#> factor(time)2       -117.500    569.739  -0.206   0.8425  
#> factor(time)3       -158.750    753.695  -0.211   0.8392  
#> factor(time)4       2618.219    936.655   2.795   0.0267 *
#> factor(time)5       2118.219    936.655   2.261   0.0582 .
#> factor(model)a3    -2296.476   2100.974  -1.093   0.3106  
#> factor(model)Corsa        NA         NA      NA       NA  
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Residual standard error: 805.7 on 7 degrees of freedom
#> Multiple R-squared:  0.8291, Adjusted R-squared:  0.6827 
#> F-statistic: 5.662 on 6 and 7 DF,  p-value: 0.01921


# make new individual index from model and trim
cars$modeltrim <- paste(cars$model, cars$trim, sep = "_")

# formula one-way within via LSDV
form <- sales ~ tax +  factor(modeltrim)
summary(lm(form, data = cars))
#> 
#> Call:
#> lm(formula = form, data = cars)
#> 
#> Residuals:
#>     Min      1Q  Median      3Q     Max 
#> -1000.0  -131.2    12.5   108.3  1000.0 
#> 
#> Coefficients: (1 not defined because of singularities)
#>                             Estimate Std. Error t value Pr(>|t|)   
#> (Intercept)                 2717.647    518.058   5.246  0.00119 **
#> tax                           -7.255      3.319  -2.186  0.06509 . 
#> factor(modeltrim)a1_Sport  -1025.490    463.745  -2.211  0.06267 . 
#> factor(modeltrim)a3_Corss    939.804   1396.610   0.673  0.52259   
#> factor(modeltrim)a3_Cross    959.804   1396.610   0.687  0.51405   
#> factor(modeltrim)a3_Street  1680.294   1637.233   1.026  0.33890   
#> factor(modeltrim)Corsa_O1   1645.098    584.414   2.815  0.02596 * 
#> factor(modeltrim)Corsa_O2         NA         NA      NA       NA   
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Residual standard error: 618.1 on 7 degrees of freedom
#> Multiple R-squared:  0.8994, Adjusted R-squared:  0.8132 
#> F-statistic: 10.44 on 6 and 7 DF,  p-value: 0.003392


# one-way within model via plm
library(plm)
plm(sales ~ tax, 
    data = cars,
    model = "within",
    index = c("modeltrim", "time"),
    effect = "individual")
#> Error in plm.fit(data, model, effect, random.method, random.models, random.dfcor, : empty model

plm(sales ~ tax,
    data = cars,
    model = "within",
    index = c("modeltrim", "time"),
    effect = "twoways")
#> Error in plm.fit(data, model, effect, random.method, random.models, random.dfcor, : empty model
# tax does not vary per modeltrim (does not vary per individual) - within model non-estimable
pvar(cars, index = c("modeltrim", "time"))
#> no time variation:       make model trim tax modeltrim 
#> no individual variation: make time
#
# look at variable tax after one-way within transformation
pcars <- pdata.frame(cars, index = c("modeltrim", "time"))
mf <- model.frame(pcars, sales ~ tax)
model.matrix(mf, model = "within")[ , "tax"]
#>  1  2  3  4  5  6  7  8  9 10 11 12 13 14 
#>  0  0  0  0  0  0  0  0  0  0  0  0  0  0
Related