如何更改data.table中因子列的级别

Ric*_*rta 48 r data.table

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

  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"
Run Code Online (Sandbox Code Playgroud)

我正在寻找类似的东西:

mydt[, levels(value) <- c("X", "Y", "Z")]
Run Code Online (Sandbox Code Playgroud)

但是,当然,上述行不起作用.

    # 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
Run Code Online (Sandbox Code Playgroud)

Jus*_*tin 61

你仍然可以用传统方式设置它们:

levels(mydt$value) <- c(...)
Run Code Online (Sandbox Code Playgroud)

这应该足够快,除非mydt非常大,因为传统语法复制整个对象.你也可以玩非保理和重构游戏......但无论如何也没有人喜欢那个游戏.

要通过引用更改级别而不复制以下内容mydt:

setattr(mydt$value,"levels",c(...))
Run Code Online (Sandbox Code Playgroud)

但一定要指定一个有效的水平向量(character足够长度的类型),否则你最终会得到一个无效因素(levels<-做一些检查以及复制).

  • @RicardoSaporta看起来很棒.也许我可以将它添加到data.table?我称之为`setlevels`并稍微改变界面:`setlevels(DT $ colname,newlevels)`,如果可以的话?人们经常要求`set*`函数来处理`data.frame`,他们可以做. (7认同)
  • @MattDowle,`setlevels()`到底被放入了?我找不到任何其他文件. (6认同)
  • @skan看起来`setlevels()`在C级内部存在但从未暴露过.我刚刚提交了[FR#2219](https://github.com/Rdatatable/data.table/issues/2219).如果您想添加它,拉取请求将非常受欢迎.在此期间可以使用`setattr`.为了给几个列分配相同的级别,我认为`for`循环对于代码的未来读者来说是最好的,快速的,最清晰的; 如果在循环内部使用`set*`函数来避免复制. (2认同)

ekt*_*kta 6

我宁愿采用传统的重新分配方式来考虑因素

> 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
Run Code Online (Sandbox Code Playgroud)

您可能已经注意到,重新分配的内容非常直观,它检查是否正确levelgrepl以防出现模糊数学,正则表达式或类似情况)

levels(mydt$value)[levels(mydt$value)=="A"] <- "X" 这会明确检查所 考虑变量的值,levels 然后将其重新分配X(依此类推)-优势-您明确知道标记了什么的内容。

我发现重命名级别是levels(mydt$value) <- c("X","Y","Z")非常不直观的,因为它只是将其分配X给数据中的SEES的第一个级别(因此顺序确实很重要)

PPS:如果级别过多,请使用循环结构。

  • 但是这种肉的每一行都会复制“ mydt”的整个部分。如果`mydt`在RAM中有20GB,那将是60GB。data.table用于提高内存效率及其语法。贾斯汀(Justin)的答案_完全不复制20GB,它只是直接就地更改级别。这里表达的有效关注的答案是将好的逻辑包装到新的函数setlevels中,其本质上与setnames的安全性,鲁棒性和直观性相似。 (4认同)
  • 即使您只触摸`mydt`的一小部分,当您使用`&lt;-`时,R也会复制整个`mydt`。在这种情况下,通过`levels &lt;-`函数分配给`levels'并复制整个`mydt`。因此,在`data.table`中,我们提供`:=`和`set *`函数以通过引用分配,仅更改要更改的mydt的一小部分,而没有副本。setattr是set *功能之一。 (3认同)

Bry*_*son 5

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

f <- factor(c("a","b"))
levels(f) <- list(C = "C", D = "a", B = "b")
Run Code Online (Sandbox Code Playgroud)

(修改自?levels