在 R 中将面板数据转换为长整型

Convert Panel Data to Long in R

我目前的数据是 1920 年到 2018 年之间的导弹。目标是衡量一个国家从 1920 年到 2018 年每年部署不同种类导弹的能力。出现的问题是每个数据都有多个观测值国家,经常每年。这会产生问题,因为例如,如果一个国家在 1970 年采用了一种空对空导弹并进口,然后在 1980 年开发了一种空对空和空对地导弹并在国内生产,则需要反映这种变化。目标是每个国家每年都有一个独特的 row/observation。还应该注意的是,假设国家可以在 1970 年生产 Air to air,例如,他们可以这样做到 2018 年。 当前:

YearAcquired CountryCode CountryName Domestic AirtoAir
     2014         670    Saudi Arabia    0        1
     2017         670    Saudi Arabia    1        1
     2016          2    United States    1        1

期望:

YearAcquired CountryCode CountryName Domestic AirtoAir
     2014         670    Saudi Arabia    0        1
     2015         670    Saudi Arabia    0        1
     2016         670    Saudi Arabia    0        1
     2017         670    Saudi Arabia    1        1
     2018         670    Saudi Arabia    1        1
     2016          2    United States    0        1
     2017          2    United States    0        1
     2018          2    United States    0        1

注意:有很多条目,所以我希望它为每个国家/地区生成从 1920 年到 2018 年的数据,即使它们会有直零。这不是必需的,但会很棒!

您可以使用可用的国家/地区名称和代码创建新的数据框,并与现有数据执行左连接。对于每个国家和代码,这将为您提供 1920 到 2018 年,将 NA 留在您没有可用数据的地方,但您可以轻松地替换它们,因为您希望数据结构化。

# df is your initial dataframe 
countries <- df$CountryName
codes <- df

new_df <- data.frame(YearAcquired = seq(1920, 2018, 1),
           CountryName = df$CountryName
           CountryCode = df$CountryCode)
new_df <- left_join(new_df, df)

您可以通过几个步骤完成此操作:

  1. 创建所有年份和国家的组合(SQL 中的 CROSS JOIN)
  2. LEFT JOIN 这些组合与可用数据
  3. 使用 zoo::na.locf() 之类的函数将 NA 值替换为每个国家/地区的最后一个已知值。

第一步常见:

df <- read.table(text = 'YearAcquired CountryCode CountryName Domestic AirtoAir
     2014         670    "Saudi Arabia"    0        1
     2017         670    "Saudi Arabia"    1        1
     2016          2    "United States"    1        1', header = TRUE, stringsAsFactors = FALSE)

combinations <- merge(data.frame(YearAcquired = seq(1920, 2018, 1)),
                      unique(df[,2:3]), by = NULL)

对于第 2 步和第 3 步,此处使用 dplyr

的解决方案
library(dplyr)
library(zoo)

df <- left_join(combinations, df) %>%
      group_by(CountryCode) %>%
      mutate(Domestic = na.locf(Domestic, na.rm = FALSE),
             AirtoAir = na.locf(AirtoAir, na.rm = FALSE))

一种解决方案使用 data.table

library(data.table)
library(zoo)

setDT(df)
setDT(combinations)

df <- df[combinations, on = c("YearAcquired", "CountryCode", "CountryName")]
df <- df[, na.locf(.SD, na.rm = FALSE), by = "CountryCode"]

使用 ( and )...

如果您只需要填写每个国家/地区的内部年份...

df <- read.table(header = TRUE, as.is = TRUE, text = "
YearAcquired  countrycode   CountryName    Domestic  AirtoAir
2014          670           'Saudi Arabia'    0         1
2017          670           'Saudi Arabia'    1         1
2016          2             'United States'   1         1
")

library(dplyr)
library(tidyr)

df %>% 
  group_by(countrycode) %>% 
  complete(YearAcquired = full_seq(YearAcquired, 1), countrycode, CountryName) %>% 
  arrange(countrycode, YearAcquired) %>% 
  fill(Domestic, AirtoAir)

#> # A tibble: 5 x 5
#> # Groups:   countrycode [2]
#>   YearAcquired countrycode CountryName   Domestic AirtoAir
#>          <dbl>       <int> <chr>            <int>    <int>
#> 1         2016           2 United States        1        1
#> 2         2014         670 Saudi Arabia         0        1
#> 3         2015         670 Saudi Arabia         0        1
#> 4         2016         670 Saudi Arabia         0        1
#> 5         2017         670 Saudi Arabia         1        1

如果您想将每个国家/地区扩展到数据集中找到的所有年份...

df <- read.table(header = TRUE, as.is = TRUE, text = "
YearAcquired  countrycode   CountryName    Domestic  AirtoAir
2014          670           'Saudi Arabia'    0         1
2017          670           'Saudi Arabia'    1         1
2016          2             'United States'   1         1
")

library(dplyr)
library(tidyr)

df %>% 
  complete(YearAcquired = full_seq(YearAcquired, 1), 
           nesting(countrycode, CountryName)) %>% 
  group_by(countrycode) %>% 
  arrange(countrycode, YearAcquired) %>% 
  fill(Domestic, AirtoAir) %>% 
  mutate_at(vars(Domestic, AirtoAir), funs(if_else(is.na(.), 0L, .)))

#> # A tibble: 8 x 5
#> # Groups:   countrycode [2]
#>   YearAcquired countrycode CountryName   Domestic AirtoAir
#>          <dbl>       <int> <chr>            <int>    <int>
#> 1         2014           2 United States        0        0
#> 2         2015           2 United States        0        0
#> 3         2016           2 United States        1        1
#> 4         2017           2 United States        1        1
#> 5         2014         670 Saudi Arabia         0        1
#> 6         2015         670 Saudi Arabia         0        1
#> 7         2016         670 Saudi Arabia         0        1
#> 8         2017         670 Saudi Arabia         1        1