【问题标题】:data.table adding column to several data tables in loopdata.table 在循环中向多个数据表添加列
【发布时间】:2016-05-05 20:26:31
【问题描述】:

我遇到了一个非常简单的问题,但还没有找到解决方法。我得到了来自不同商店的大约 200 张表,并且必须将整个数据体作为一个单元进行分析。由于表相当大,我正在使用data.table::fread。我的计划是加载每个表,rbind将它们放在一起并继续分析。为了保留哪个商店是哪个商店,我想为每个表添加一个变量 store,并带有商店的名称(与表相同)。为此,我计划使用表名运行 for 循环并创建新变量。

# I'll create some dummy data tables here. In reality they're loaded with fread
library(data.table)
centro <- data.table(x=letters[1:5], y = 1:5)
sur <- data.table(x=letters[2:7], y = 3:8)
...
norte <- data.table(x=letters[2:10], y = 3:11)

我需要每个表都有一个变量“store”,其值为“centro”、“sur”或“norte”,具体取决于它所属的商店。所以我尝试了:

st <- c("centro", "sur", "norte")
for (i in st){
   i[, store := i]
}

当然,这不起作用(它抱怨“我不是 data.table”)。然后我尝试创建一个列表并通过lapply尝试:

sts <- list(centro, sur, norte)
sts <- lapply(sts, function(z) z[, store := names(z)])

这不起作用,因为names(z) 是“x”、“y”和“store”。

如何使用循环(或 *pply 函数)而不是手动执行 xyz[, store := "xyz"] 来做到这一点?

旁注

  • 变量的命名几乎不可能调整,并且不遵循任何模式(不像 store_1、store_2 等)
  • 我可能对我描述的替代方案有很大的偏见。任何其他可行的方法都可以。

【问题讨论】:

  • 您的带有i[, store := i] 的代码块是不可重现的,i 是一个字符标量,您正在使用[ 对其进行子集化,并尝试在内部使用:=。从那里开始。

标签: r data.table


【解决方案1】:

最好的方法是将对象存储在列表中,然后通过索引列表来访问它们。

library(data.table)

my_dts <- list (
  centro = data.table(x=letters[1:5], y = 1:5),
  sur = data.table(x=letters[2:7], y = 3:8),
  norte = data.table(x=letters[2:10], y = 3:11)
)

st <- c("centro", "sur", "norte")
for (i in st) my_dts[[i]][, store := i]

【讨论】:

  • 这个很明显,谢谢。这些是在太多小时/太少的咖啡之后不会想到的那种解决方案。
  • my_dts[[i]][, store := i] 实际上不起作用。 for (i in 1:length(my_dts)) my_dts[[i]][, store := st[i]] 确实如此。
  • 为我工作。如果列表有名称,您可以按名称索引列表,如上例所示。你也可以按数字索引,所以你的评论也有效
  • 当然,我只是列出了工作区中的变量,所以这就是它不起作用的原因。正如你所提到的,确实如此。
【解决方案2】:

直接回答,变化不大

get 包裹在i 周围,并将值分配回i 中包含的名称。

for (i in st){
  assign(i, get(i)[, store := i])
}

更好的系统,一些操作

将表存储在列表中并使用@arvi1000 建议的方法。 几个非常明显的优势

  1. 由于您的各个商店都是列表下的所有项目,因此您的工作区中的对象减少了大约 200 个,当您 ls() 时这很好。
  2. 您可以使用 lapply 代替 for 循环,这通常意味着代码更简洁
  3. 您可以使用AllStore &lt;- do.call(what = rbind, args = my_dts)

'More Better',回到开头

  • 当你从不同的 store 中读取数据时,设置 然后store 变量。使用 lapply 和匿名函数来执行此操作。
  • 不要费心保存my_dts 列表,因为 这只是将保存在all_stores 中的重复数据 无论如何。

巧合的是,上周我遇到了类似的业务问题,这就是我使用的方法,处理了大约 250 个 csv。我认为您的代码如下所示。

st <- c("centro", "sur", "norte")

all_stores <- do.call(
  what = rbind,
  args = lapply(
    X = st,
    FUN = function(storeNM){
      temp <- fread(input = paste0(storeNM, '.csv'))
      temp[, store := storeNM]
      return(temp)
    }
  )
)

【讨论】:

  • 感谢您介绍get,这正是我正在寻找的功能。但是,它不像您建议的那样工作 (get(i)[, store := i]),因为它实际上并没有修改对象。需要分配:i &lt;- get(i)[, store := i]
  • 在我之前的评论中,应该是assign(i, get(i)[, store := i])
  • 真实陈述。我没有想过(但我应该有); get 按值返回,这意味着 [.data.table 的按引用质量都消失了。固定。
【解决方案3】:

试试这个:

for (i in st)
{
eval(parse(text=i))[, ':='(store = i)]
}

再想一想,这可能更好。假设您使用s&lt;-lapply(filepathlist,fread) 读取了所有数据集,然后使用:

st <- c("centro", "sur", "norte") # need to change this to a list of datasets you read in    
st2<-lapply(st,function(i){eval(parse(text=i))[, ':='(store = i)]})
st3<-rbindlist(st2)

输出:

    > st3
        x  y  store
     1: a  1 centro
     2: b  2 centro
     3: c  3 centro
     4: d  4 centro
     5: e  5 centro
     6: b  3    sur
     7: c  4    sur
     8: d  5    sur
     9: e  6    sur
    10: f  7    sur
    11: g  8    sur
    12: b  3  norte
    13: c  4  norte
    14: d  5  norte
    15: e  6  norte
    16: f  7  norte
    17: g  8  norte
    18: h  9  norte
    19: i 10  norte
    20: j 11  norte

【讨论】:

  • 与@Adam 的回答中的get 一样,eval(parse(text = i))[, store := i] 部分实际上并没有改变变量,它确实需要赋值:assign(i, eval(parse(text = i))[, store := i])
  • 同意,需要赋值,eval(parse(...)) 只会评估对变量的引用。我发现 @arvi1000 使用数据集列表更好地回答。
猜你喜欢
  • 2017-06-22
  • 1970-01-01
  • 2018-12-05
  • 1970-01-01
  • 2012-07-03
  • 2013-11-07
  • 2022-01-14
  • 1970-01-01
相关资源
最近更新 更多