【问题标题】:How can i add more columns in dataframe by for loop如何通过 for 循环在数据框中添加更多列
【发布时间】:2019-12-26 12:15:19
【问题描述】:

我是 R 的初学者。我需要将一些 Eviews 代码转移到 R。有一些循环代码可以在 Eviews 的数据中添加 10 个或更多列\具有某些功能的变量。

这里是 eviews 示例代码来估计 deflator:

for %x exp con gov inv cap ex im
frml def_{%x} = gdp_{%x}/gdp_{%x}_r*100
next 

我使用了 dplyr 包并使用了 mutate 函数。但是添加很多变量是非常困难的。

library(dplyr)
nominal_gdp<-rnorm(4)
nominal_inv<-rnorm(4)
nominal_gov<-rnorm(4)
nominal_exp<-rnorm(4)

real_gdp<-rnorm(4)
real_inv<-rnorm(4)
real_gov<-rnorm(4)
real_exp<-rnorm(4)   

df<-data.frame(nominal_gdp,nominal_inv,
nominal_gov,nominal_exp,real_gdp,real_inv,real_gov,real_exp)

 df<-df %>% mutate(deflator_gdp=nominal_gdp/real_gdp*100,
 deflator_inv=nominal_inv/real_inv, 
 deflator_gov=nominal_gov/real_gov,
 deflator_exp=nominal_exp/real_exp)

 print(df)

请在 R 循环中帮我解决这个问题。

【问题讨论】:

  • 检查apply()
  • 我重命名了我的帖子。你能重读我的问题吗?
  • 很好的编辑。在您当前的问题中,您试图自动化的内容并不明显。为清楚起见,您应该添加许多这样的 deflatorX 以从 nominalXrealX 进行计算,因此您希望自动创建许多 deflators。
  • 如果我们知道列的顺序与您的示例中一样,那么也许可以试试? df[ 1:2 ] / df[ 3:4 ] * 100

标签: r for-loop dplyr eviews


【解决方案1】:

答案是您的数据没有达到应有的“整洁”程度。

这就是你所拥有的(为了清楚起见,添加了观察 ID):

library(dplyr)

df <- data.frame(nominal_gdp = rnorm(4),
                 nominal_inv = rnorm(4),
                 nominal_gov = rnorm(4),
                 real_gdp = rnorm(4),
                 real_inv = rnorm(4),
                 real_gov = rnorm(4))
df <- df %>%
  mutate(obs_id = 1:n()) %>%
  select(obs_id, everything())

给出:

   obs_id nominal_gdp nominal_inv nominal_gov    real_gdp   real_inv  real_gov
 1      1  -0.9692060  -1.5223055 -0.26966202  0.49057546  2.3253066 0.8761837
 2      2   1.2696927   1.2591910  0.04238958 -1.51398652 -0.7209661 0.3021453
 3      3   0.8415725  -0.1728212  0.98846942 -0.58743294 -0.7256786 0.5649908
 4      4  -0.8235101   1.0500614 -0.49308092  0.04820723 -2.0697008 1.2478635

考虑一下你是否有,df2:

   obs_id variable        real     nominal
1       1      gdp  0.49057546 -0.96920602
2       2      gdp -1.51398652  1.26969267
3       3      gdp -0.58743294  0.84157254
4       4      gdp  0.04820723 -0.82351006
5       1      inv  2.32530662 -1.52230550
6       2      inv -0.72096614  1.25919100
7       3      inv -0.72567857 -0.17282123
8       4      inv -2.06970078  1.05006136
9       1      gov  0.87618366 -0.26966202
10      2      gov  0.30214534  0.04238958
11      3      gov  0.56499079  0.98846942
12      4      gov  1.24786355 -0.49308092

那你想做的事就是小事:

df2 %>% mutate(deflator = real / nominal)
   obs_id variable        real     nominal    deflator
1       1      gdp  0.49057546 -0.96920602 -0.50616221
2       2      gdp -1.51398652  1.26969267 -1.19240392
3       3      gdp -0.58743294  0.84157254 -0.69801819
4       4      gdp  0.04820723 -0.82351006 -0.05853872
5       1      inv  2.32530662 -1.52230550 -1.52749012
6       2      inv -0.72096614  1.25919100 -0.57256297
7       3      inv -0.72567857 -0.17282123  4.19901294
8       4      inv -2.06970078  1.05006136 -1.97102841
9       1      gov  0.87618366 -0.26966202 -3.24919196
10      2      gov  0.30214534  0.04238958  7.12782060
11      3      gov  0.56499079  0.98846942  0.57158146
12      4      gov  1.24786355 -0.49308092 -2.53074800

所以问题就变成了:我们如何获得与 dplyr 兼容的良好 data.frame。

您需要使用tidyr::gather 收集您的数据。但是,因为您要收集 2 组变量(实际值和名义值),所以这并不简单。我已经分两步完成了,不过可能有更好的方法。

real_vals <- df %>%
  select(obs_id, starts_with("real")) %>%
  # the line below is where the magic happens
  tidyr::gather(variable, real, starts_with("real")) %>%
  # extracting the variable name (by erasing up to the underscore)
  mutate(variable = gsub(variable, pattern = ".*_", replacement = ""))

# Same thing for nominal values
nominal_vals <- df %>%
  select(obs_id, starts_with("nominal")) %>%
  tidyr::gather(variable, nominal, starts_with("nominal")) %>%
  mutate(variable = gsub(variable, pattern = ".*_", replacement = ""))

# Merging them... Now we have something we can work with!
df2 <-
  full_join(real_vals, nominal_vals, by = c("obs_id", "variable"))

注意合并时观察id的重要性。

【讨论】:

  • 非常感谢。但我的数据有很多观察结果。循环那个操作怎么样?
  • 我不太清楚你的意思,因为这段代码可以一次性处理数千个real_XXX和nominal_XXX,而且行数不是问题(只要它保持在~1亿以下)。 “许多观察”是什么意思?
  • 我只是告诉我我多次使用这个 eviews 循环并创建了这么多列。但是在eviews中通过循环添加列是很容易的。
  • 我非常感谢你的代码,但我的老板需要我循环。
  • 在这种情况下,answer from Suliman 就是您所需要的。您可以在循环中使用deflator_fun。我会使用Reduce 而不是循环。
【解决方案2】:

我们可以 grep 匹配的名称,然后排序:

x <- colnames(df)
df[ sort(x[ (grepl("^nominal", x)) ]) ] /
  df[ sort(x[ (grepl("^real", x)) ]) ] * 100

同样,如果列已排序,那么我们可以:

df[ 1:4 ] / df[ 5:8 ] * 100

【讨论】:

    【解决方案3】:

    我们可以使用purrr::map_dfc 循环列名,然后对选定的列应用自定义函数(即与nms 中的当前名称匹配的列)

    library(dplyr)
    library(purrr)
    #Replace anything before _ with empty string
    nms <- unique(sub('.*_','',names(df)))
    #Use map if you need the ouptut as a list not a dataframe
    map_dfc(nms, ~deflator_fun(df, .x))
    

    自定义函数

    deflator_fun <- function(df, x){
      #browser()
      nx <- paste0('nominal_',x)
      rx <- paste0('real_',x)  
      select(df, matches(x)) %>% 
        mutate(!!paste0('deflator_',quo_name(x)) := !!ensym(nx) / !!ensym(rx)*100)
    }
    #Test
    deflator_fun(df, 'gdp')
          nominal_gdp     real_gdp deflator_gdp
    1  -0.3332074  0.181303480   -183.78433
    2  -1.0185754 -0.138891362    733.36121
    3  -1.0717912  0.005764186 -18593.97398
    4   0.3035286  0.385280401     78.78123
    

    注意:了解更多关于 quo_name!!ensym 的信息,它们是使用 dplyr here 进行编程的工具

    【讨论】:

      猜你喜欢
      • 2019-03-02
      • 2022-01-20
      • 1970-01-01
      • 2022-01-14
      • 2020-07-01
      • 2011-10-02
      • 1970-01-01
      • 2020-04-25
      • 1970-01-01
      相关资源
      最近更新 更多