【问题标题】:How does one change the levels of a factor column in a data.table如何更改 data.table 中因子列的级别
【发布时间】:2013-01-16 02:18:44
【问题描述】:

在data.table 中更改factor 列的级别的正确方法是什么(注意:不是数据框)

  library(data.table)
  mydt <- data.table(id=1:6, value=as.factor(c("A", "A", "B", "B", "B", "C")), key="id")

  mydt[, levels(value)]
  [1] "A" "B" "C"

我正在寻找类似的东西:

mydt[, levels(value) <- c("X", "Y", "Z")]

当然,上面的行是行不通的。

    # Actual               # Expected result
    > mydt                  > mydt
       id value                id value
    1:  1     A             1:  1     X
    2:  2     A             2:  2     X
    3:  3     B             3:  3     Y
    4:  4     B             4:  4     Y
    5:  5     B             5:  5     Y
    6:  6     C             6:  6     Z

【问题讨论】:

  • 你仍然可以用传统的方式设置它们:levels(mydt$value)
  • 我没有尝试明显的 :) 谢谢!把它作为答案,以便我接受?

标签: r data.table


【解决方案1】:

您仍然可以按传统方式设置它们:

levels(mydt$value) <- c(...)

这应该很快,除非mydt 非常大,因为传统语法会复制整个对象。你也可以玩拆解和重构的游戏……但反正没人喜欢这种游戏。

在没有mydt 副本的情况下通过引用更改级别:

setattr(mydt$value,"levels",c(...))

但一定要分配一个有效的级别向量(类型足够长的character),否则你会得到一个无效的因子(levels&lt;- 会进行一些检查和复制)。

【讨论】:

  • @MatthewDowle 感谢您的参与。setattr 正是我想要的。我已经将它包装在一个带有错误检查的函数 changeLevels 中:bit.ly/dt_changeLevels
  • @RicardoSaporta 看起来很棒。也许我可以将它添加到 data.table 中?我将其称为setlevels 并稍微更改界面:setlevels(DT$colname, newlevels),如果这样可以吗?人们经常要求 set* 函数也可以在 data.frame 上工作,他们可以做到。
  • @MattDowle,setlevels() 最后被加入了吗?我找不到任何其他文档。
  • @MattDowle 如果我想同时在多个列上使用相同的级别怎么办?有什么捷径可以做到吗?
  • @skan 看起来 setlevels() 在 C 级别内部存在,但从未暴露。我刚刚提交了FR#2219 指向这里。如果您想添加它,我们将非常欢迎您提出拉取请求。同时可以使用setattr。要将相同的级别分配给多个列,我认为 for 循环对于您的代码的未来读者来说是最好、快速和最清晰的;前提是在循环内使用set* 函数以避免复制。
【解决方案2】:

我宁愿走传统的重新分配因子的方式

> mydt$value # This we what we had originally
[1] A A B B B C
Levels: A B C
> levels(mydt$value) # just checking the levels
[1] "A" "B" "C"
**# Meat of the re-assignment**
> levels(mydt$value)[levels(mydt$value)=="A"] <- "X"
> levels(mydt$value)[levels(mydt$value)=="B"] <- "Y"
> levels(mydt$value)[levels(mydt$value)=="C"] <- "Z"
> levels(mydt$value)
[1] "X" "Y" "Z"
> mydt # This is what we wanted
   id value
1:  1     X
2:  2     X
3:  3     Y
4:  4     Y
5:  5     Y
6:  6     Z

您可能注意到,重新分配的内容非常直观,它会检查确切的level(使用grepl,以防数学模糊、正则表达式或类似情况)

levels(mydt$value)[levels(mydt$value)=="A"] &lt;- "X" 这会明确检查所考虑变量的 levels 中的值,然后将X(等等)重新分配给它 - 优点 - 你明确知道标记了什么。

我发现像 levels(mydt$value) &lt;- c("X","Y","Z") 这样重命名级别非常不直观,因为它只是将 X 分配给它在数据中看到的第一个级别(所以顺序真的很重要)

PPS : 如果层数过多,请使用循环构造。

【讨论】:

  • 但是这块肉的每一行都会复制mydt的整个。如果mydt 的 RAM 为 20GB,那么它就会流失 60GB。 data.table 用于内存效率及其语法。贾斯汀的回答根本不复制 20GB,它只是直接就地更改级别。这里表达的有效问题的答案是将这个好的逻辑包装到新函数 setlevels 中,这在精神上类似于 setnames 的安全性、稳健性和直观性。
  • @MattDowle 感谢您提及这一点。您能否提及哪个级别(mydt$value)[levels(mydt$value)=="A"]
  • 即使您只触摸了mydt 的一小部分,当您使用&lt;- 时,R 也会复制整个mydt。在这种情况下,分配给levels 是通过levels&lt;- 函数并复制整个mydt。因此,在data.table 中,我们提供:= 和set* 函数通过引用分配以仅更改您想要更改的mydt 的小部分,没有副本。 setattr 是 set* 函数之一。
  • @MattDowle 非常感谢您对两个笔记的详尽解释 + 1。我现在也“完全”理解了贾斯汀的评论。
  • 我一直在寻找这条线 LOL levels(mydt$value)[levels(mydt$value)=="A"]
【解决方案3】:

您还可以使用相关方法重命名并添加到您的关卡,这非常方便,特别是如果您正在制作一个需要以特定顺序(而不是默认顺序)提供更多信息标签的绘图:

f <- factor(c("a","b"))
levels(f) <- list(C = "C", D = "a", B = "b")

(修改自?levels)

【讨论】:

  • +1 严格来说,虽然问题是关于如何更改级别当该因素是data.table 的列时。 data.table 具有内置功能,允许添加和重命名因子,也可以通过引用来避免复制整个对象以提高速度。因子列上的:= 将自动将 RHS 添加为级别,如果它不存在的话。并且setattr 可用于通过引用更改因子列的级别(无副本)。
  • @MatthewDowle 啊,你知道我什至没有赶上data.table vs data.frame!感谢您指出了这一点。我的大多数数据集的大小都很适中,但我知道data.table,它有一些我知道我可以使用的不错的功能。
【解决方案4】:

这比 Matt Dowle 的建议更安全(因为它使用了 setattr 跳过的检查),但不会复制整个 data.table。它将替换整个列向量(而 Matt 的解决方案只替换列向量的属性),但这似乎是一个可以接受的权衡,以减少弄乱因子对象的风险。

mydt[, value:=`levels<-`(value, c("X", "Y", "Z"))]

【讨论】:

    【解决方案5】:

    更改列级别的最简单方法:

    dat$colname &lt;- as.factor(as.vector(dat$colname));

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-06-25
      • 2014-02-11
      • 2015-03-05
      • 1970-01-01
      • 1970-01-01
      • 2021-08-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多