【问题标题】:Use lapply to create new variable over multiple data frames使用 lapply 在多个数据帧上创建新变量
【发布时间】:2017-06-27 01:39:06
【问题描述】:

我已经搜索了所有 lapply 问题和解决方案,但这些解决方案似乎都没有解决和/或适用于以下问题...

我有一个列表“temp”,其中包含 100 个数据帧的名称:“sim_rep1.dat”到“sim_rep100.dat”。

每个数据框有 2000 个观测值和相同的 11 个变量:ARAND 和 w1-w10,它们都是数字。

对于所有 100 个数据帧,我正在尝试创建一个名为“ps_true”的新变量,该变量包含某些“w”变量,每个变量都有一个唯一的系数。

对我有用的 lapply 的唯一用途如下:

lapply(mget(paste0("sim_rep", 1:100,".dat")), transform, 
            ps_true = (1 + exp(-(0.8*w1 - 0.25*w2 + 0.6*w3 - 
                       0.4*w4 - 0.8*w5 - 0.5*w6  + 0.7*w7)))^-1)

当我运行上面的代码时,R 循环遍历所有 100 个数据帧,并在控制台中显示新计算的 ps_true 值。不幸的是,新列没有添加到数据框中。

当我尝试创建一个函数时,轮子完全脱落了。

我尝试了以下不同的变体:

 lapply(temp, function(x){       
        ps_true = (1 + exp(-(0.8*w1 - 0.25*w2 + 0.6*w3 - 
                       0.4*w4 - 0.8*w5 - 0.5*w6  + 0.7*w7)))^-1
   cbind(x, ps_true)
   return(x)
 })
  • FUN(X[[i]], ...) 中的错误:找不到对象 'w1' 上述函数的结果
  • x$w1 中的错误:如果我尝试引用 x$w1,$ 运算符对原子向量无效 结果
  • FUN(X[[i]], ...) 中的错误:如果我尝试引用 x[[w1]],则找不到对象 'w1'
  • x[["w1"]] 中的错误:如果我尝试引用 x[["w1"]],则会导致下标超出范围

我希望我缺少一些明显的东西。感谢您为解决这个令人沮丧的问题提出的见解和建议。

回应 Uwe 的附录:

我用来读取所有文件的代码如下:

temp = list.files(pattern='*.dat')
for (i in 1:length(temp)) {
    assign(temp[i], read.csv(temp[i], header=F,sep="",
           col.names = c("ARAND", "w1", "w2", "w3", "w4", "w5", "w6", "w7", "w8", "w9", "w10")))
}

【问题讨论】:

  • 我已经扩展了 my answer 以涵盖所有 data.frames 都存储在磁盘上的单独文件中的情况。建议的方法将一次将所有文件读入一个大型 data.table。
  • 抱歉格式化。我已经编辑了我原来的问题,并在底部包含了这段代码。再次感谢,Uwe!

标签: r dataframe transform lapply


【解决方案1】:

根据 OP,有 100 个 data.frames 具有相同的列名。 OP 希望使用完全相同的公式在所有 data.frames 中创建一个新列。

这表明数据结构设计存在根本缺陷。我想,没有数据库管理员会创建 100 个相同的表,其中只有数据内容不同。相反,他将创建 one 表,其中包含一个标识每一行来源的附加列。然后,所有后续操作都将应用于一个表,而不是对多个表中的每一个重复。

在 R 中,data.table 包具有方便的 rbindlist() 函数,可用于此目的:

library(data.table)   # CRAN version 1.10.4 used
# get list of data.frames from the given names and
# combine the rows of all data sets into one large data.table
DT <- rbindlist(mget(temp), idcol = "origin")
# now create new column for all rows across all data sets
DT[, ps_true := (1 + exp(-(0.8*w1 - 0.25*w2 + 0.6*w3 - 
                            0.4*w4 - 0.8*w5 - 0.5*w6  + 0.7*w7)))^-1]
DT
                origin ARAND   w1   w2   w3   w4   w5   w6   w7   w8   w9  w10   ps_true
     1:   sim_rep1.dat  -0.6 -0.5  0.2 -0.7  0.5  2.4 -0.2 -0.9 -1.1  0.3 -0.8 0.0287485
     2:   sim_rep1.dat  -0.2  0.2  0.7  1.0  1.8 -0.2  0.8  0.3 -1.3 -1.6 -0.2 0.4588433
     3:   sim_rep1.dat   1.6 -0.5  0.7 -0.7 -1.7  0.9 -1.2 -1.0  1.1 -0.3 -2.1 0.2432395
     4:   sim_rep1.dat   0.1  1.2 -1.3 -0.1  0.3 -0.6  0.4  0.3  0.8 -1.2 -1.7 0.8313184
     5:   sim_rep1.dat   0.1  0.2 -2.0  0.6 -0.3  0.2  0.2  0.5 -0.9 -0.8 -1.1 0.7738186
    ---                                                                                 
199996: sim_rep100.dat   0.1 -1.4  1.6 -0.7 -1.0 -0.6  0.8 -0.6 -0.5 -0.4 -0.8 0.1323889
199997: sim_rep100.dat   0.3  1.3 -2.4 -0.7 -0.4  0.0  1.0 -0.2  1.0 -0.1  0.3 0.6769959
199998: sim_rep100.dat   0.3  1.2  0.0 -1.3 -0.8 -0.7 -0.3  0.1  0.9  0.9 -1.3 0.7824498
199999: sim_rep100.dat   0.5 -0.7  0.2  0.5  1.1 -0.3  0.3 -0.5 -0.8  1.9 -0.7 0.2669799
200000: sim_rep100.dat  -0.5  1.1  0.8  0.2 -0.6 -0.5 -0.4  1.1 -1.8  0.9 -1.3 0.9175867

DT 现在包含 200 K 行。性能无需担心,因为 data.table 旨在高效处理大型(甚至更大)数据。


可以识别每行的来源,以防需要单独处理各个数据集的数据。例如,

DT[origin == "sim_rep47.dat"]
             origin ARAND   w1   w2   w3   w4   w5   w6   w7   w8   w9  w10   ps_true
   1: sim_rep47.dat  -0.6 -0.5  0.2 -0.7  0.5  2.4 -0.2 -0.9 -1.1  0.3 -0.8 0.0287485
   2: sim_rep47.dat  -0.2  0.2  0.7  1.0  1.8 -0.2  0.8  0.3 -1.3 -1.6 -0.2 0.4588433
   3: sim_rep47.dat   1.6 -0.5  0.7 -0.7 -1.7  0.9 -1.2 -1.0  1.1 -0.3 -2.1 0.2432395
   4: sim_rep47.dat   0.1  1.2 -1.3 -0.1  0.3 -0.6  0.4  0.3  0.8 -1.2 -1.7 0.8313184
   5: sim_rep47.dat   0.1  0.2 -2.0  0.6 -0.3  0.2  0.2  0.5 -0.9 -0.8 -1.1 0.7738186
  ---                                                                                
1996: sim_rep47.dat   0.1 -1.4  1.6 -0.7 -1.0 -0.6  0.8 -0.6 -0.5 -0.4 -0.8 0.1323889
1997: sim_rep47.dat   0.3  1.3 -2.4 -0.7 -0.4  0.0  1.0 -0.2  1.0 -0.1  0.3 0.6769959
1998: sim_rep47.dat   0.3  1.2  0.0 -1.3 -0.8 -0.7 -0.3  0.1  0.9  0.9 -1.3 0.7824498
1999: sim_rep47.dat   0.5 -0.7  0.2  0.5  1.1 -0.3  0.3 -0.5 -0.8  1.9 -0.7 0.2669799
2000: sim_rep47.dat  -0.5  1.1  0.8  0.2 -0.6 -0.5 -0.4  1.1 -1.8  0.9 -1.3 0.9175867

提取属于数据集sim_rep47.dat的所有行。

数据

为了测试和演示,我使用以下代码创建了 100 个示例 data.frame:

# create vector of file names
temp <- paste0("sim_rep", 1:100, ".dat")
# create one sample data.frame
nr <- 2000L
nc <- 11L
set.seed(123L)
foo <- as.data.frame(matrix(round(rnorm(nr * nc), 1), nrow = nr))
names(foo) <- c("ARAND", paste0("w", 1:10))
str(foo)
# create 100 individually named data.frames by "copying" foo
for (t in temp) assign(t, foo)
# print warning message on using assign
fortunes::fortune(236)
# verify objects have been created
ls()

附录:一次读取所有文件

OP 已将类似于典型文件名的单个 data.frames 命名为 sim_rep1.datsim_rep2.dat 等。以防 OP 确实在磁盘上有 100 个文件,我想建议一种一次读取所有文件的方法。假设所有文件都存储在一个目录中。

# path to data directory
data_dir <- file.path("path", "to", "data", "directory")
# create vector of file paths
files <- dir(data_dir, pattern = "sim_rep\\d+\\.dat", full.names = TRUE)
# read all files and create one large data.table
# NB: it might be necessary to add parameters to fread() 
# or to use another file reader depending on the file type
DT <- rbindlist(lapply(files, fread), idcol = "origin")
# rename origin to contain the file names without path
DT[, origin := factor(origin, labels = basename(files))]
DT
               origin ARAND   w1   w2   w3   w4   w5   w6   w7   w8   w9  w10   ps_true
     1:  sim_rep1.dat  -0.6 -0.5  0.2 -0.7  0.5  2.4 -0.2 -0.9 -1.1  0.3 -0.8 0.0287485
     2:  sim_rep1.dat  -0.2  0.2  0.7  1.0  1.8 -0.2  0.8  0.3 -1.3 -1.6 -0.2 0.4588433
     3:  sim_rep1.dat   1.6 -0.5  0.7 -0.7 -1.7  0.9 -1.2 -1.0  1.1 -0.3 -2.1 0.2432395
     4:  sim_rep1.dat   0.1  1.2 -1.3 -0.1  0.3 -0.6  0.4  0.3  0.8 -1.2 -1.7 0.8313184
     5:  sim_rep1.dat   0.1  0.2 -2.0  0.6 -0.3  0.2  0.2  0.5 -0.9 -0.8 -1.1 0.7738186
    ---                                                                                
199996: sim_rep99.dat   0.1 -1.4  1.6 -0.7 -1.0 -0.6  0.8 -0.6 -0.5 -0.4 -0.8 0.1323889
199997: sim_rep99.dat   0.3  1.3 -2.4 -0.7 -0.4  0.0  1.0 -0.2  1.0 -0.1  0.3 0.6769959
199998: sim_rep99.dat   0.3  1.2  0.0 -1.3 -0.8 -0.7 -0.3  0.1  0.9  0.9 -1.3 0.7824498
199999: sim_rep99.dat   0.5 -0.7  0.2  0.5  1.1 -0.3  0.3 -0.5 -0.8  1.9 -0.7 0.2669799
200000: sim_rep99.dat  -0.5  1.1  0.8  0.2 -0.6 -0.5 -0.4  1.1 -1.8  0.9 -1.3 0.9175867

所有数据集现在都存储在一个包含 200 k 行的大型 data.table DT 中。但是,数据集的顺序不同,因为files 按字母顺序排序,即,

head(files)
[1] "./data/sim_rep1.dat"   "./data/sim_rep10.dat"  "./data/sim_rep100.dat"
[4] "./data/sim_rep11.dat"  "./data/sim_rep12.dat"  "./data/sim_rep13.dat"

【讨论】:

  • 谢谢,Uwe。是的,需要保留数据集以便单独运行其他分析。您的 rbindlist 解决方案运行良好。
【解决方案2】:

可能只需要单括号。

test = data.frame('w1' = c(1,2,3),'w2' = c(2,3,4))
temp = list(test,test,test)
temp2 = lapply(temp,function(x){cbind(x,setNames(x['w1'] + x['w2'],'ps_true'))})


temp2
[[1]]
   w1 w2 ps_true
1  1  2       3
2  2  3       5
3  3  4       7

[[2]]
   w1 w2 ps_true
1  1  2       3
2  2  3       5
3  3  4       7

[[3]]
   w1 w2 ps_true
1  1  2       3
2  2  3       5
3  3  4       7

【讨论】:

  • 当我使用临时列表键入相同的 temp2 分配时,我收到以下信息:x["w1"] + x["w2"] 中的错误:二进制的非数字参数运算符
  • 如果以上 3 行有效,则可能是数据问题。在您检查前 2 个数据帧在您的 temp 中是否正常后,尝试使用 temp[1:2] 相同的代码
  • “二元运算符的非数字参数”通常发生在您的数据框类为非数字时
  • 谢谢,hjw。我最终使用了 rbindlist。它允许我保留原始数据框的信息,但将数据合并到一个表中。感谢您的洞察力。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-11-08
  • 2019-11-22
  • 2020-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多