当前位置: 首页 > 知识库问答 >
问题:

数据表:如何基于包含列名的分组唯一行值更改列值

孔冥夜
2023-03-14

我有一个包含~18^6行的data.table,我需要通过ID获取CLASS的唯一值,并将它们各自的列设置为1,如下面的婴儿示例所示

DT <- data.table::data.table(ID=c("1","1","1","2","2"),
                             CLASS=c("a","a","b","c","b"),
                             a=c(0,0,0,0,0),
                             b=c(0,0,0,0,0),
                             c=c(0,0,0,0,0))

### Start with this
ID CLASS a b c
1      a 0 0 0
1      a 0 0 0
1      b 0 0 0
2      c 0 0 0
2      b 0 0 0

### Want this
ID CLASS a b c
1      a 1 1 0
1      a 1 1 0
1      b 1 1 0
2      c 0 1 1
2      b 0 1 1

我的第一反应是尝试下面的代码,但发现它会将所有列设置为1,因为唯一(DT$CLASS)本质上包含所有ID的所有唯一值,并且不会通过“分组”参数传递。

### Tried this
DT[,unique(DT$CLASS):=1,by=ID]

### Got this
ID CLASS a b c
1      a 1 1 1
1      a 1 1 1
1      b 1 1 1
2      c 1 1 1
2      b 1 1 1

我一直在努力充分利用data.table的潜力和速度,希望仅使用data.table参数中的命令来创建所需的输出。

有人可以帮助我编写正确的代码,只使用数据。表命令/参数,以便我的第j个索引仅包含唯一值(按ID),并将相应列设置为1?

后续问题:

假设每一行都有一个关联的日期RXDATE,我想为所有类值创建各自的列名,每个类的ID都包含最小的RXDATE。我也可以为此求助于dcast吗?

### Start with this
ID CLASS a b c RXDATE
1      a 1 1 0 1-1-99
1      a 1 1 0 1-2-99
1      b 1 1 0 1-3-99
2      c 0 1 1 5-4-00
2      b 0 1 1 6-5-01

### Want this
ID CLASS a b c RXDATE   a_DT   b_DT   c_DT
1      a 1 1 0 1-1-99 1-1-99 1-3-99     NA
1      a 1 1 0 1-2-99 1-1-99 1-3-99     NA
1      b 1 1 0 1-3-99 1-1-99 1-3-99     NA
2      c 0 1 1 5-4-00     NA 6-5-01 5-4-00
2      b 0 1 1 6-5-01     NA 6-5-01 5-4-00

共有3个答案

马弘益
2023-03-14

另一种可能的方法:

idx <- DT3[, CJ(I=.I, J=match(unique(CLASS), names(DT))), by=ID]
setDF(DT3)
DT3[as.matrix(idx[, .(I, J)])] <- 1L
setDT(DT3)[]
姚臻
2023-03-14

这里有一个选择。

unique_wide <- dcast(DT[, unique(CLASS), by = ID], ID ~ V1, value.var = "V1")

classes <- setdiff(names(unique_wide), "ID")
unique_wide[, (classes) := lapply(.SD, function(col) { ifelse(is.na(col), 0L, 1L) }),
            .SDcols = classes]

DT[, (classes) := unique_wide[.SD, classes, on = "ID", with = FALSE]]
DT[]
   ID CLASS a b c
1:  1     a 1 1 0
2:  1     a 1 1 0
3:  1     b 1 1 0
4:  2     c 0 1 1
5:  2     b 0 1 1

我们首先使用DT[,唯一(CLASS), by=ID]获取可能的唯一值。请注意,您可以直接引用j中的列,而无需$。我们可以将其重塑为宽格式以获得如下内容:

   ID    a b    c
1:  1    a b <NA>
2:  2 <NA> b    c

接下来的两行只是将值转换成整数,否则将< code>NA设置为0和1。

> unique_wide
   ID a b c
1:  1 1 1 0
2:  2 0 1 1

然后应用这个习语,就像嵌套连接一样。在这种情况下,连接是基于< code>ID完成的,因此在< code>DT和< code>unique_wide之间匹配的所有行都使用< code>unique_wide中的内容更新非< code>ID列的值。另请注意,< code>with = FALSE对于根据包含字符向量的变量选择列很有用。

顺便说一句,请注意,如果您将表声明为

DT <- data.table::data.table(ID=c("1","1","1","2","2"),
                             CLASS=c("a","a","b","c","b"))

上面的代码仍然有效。

党俊健
2023-03-14

使用dcast合并您还可以执行以下操作:

DT <- data.table::data.table(ID=c("1","1","1","2","2"),
                             CLASS=c("a","a","b","c","b"),
                             a=c(0,0,0,0,0),
                             b=c(0,0,0,0,0),
                             c=c(0,0,0,0,0))

# dcast to convert to wide
DT_dcast <- dcast(DT[, .(ID, CLASS)], ID ~ CLASS, fun.aggregate = function(x) length(unique(x)), value.var = "CLASS")
DT_dcast
   ID a b c
1:  1 1 1 0
2:  2 0 1 1

# Then merge with the original data.table
DT_m <- merge(DT[, .(ID, CLASS)], DT_dcast, by = "ID")
DT_m
   ID CLASS a b c
1:  1     a 1 1 0
2:  1     a 1 1 0
3:  1     b 1 1 0
4:  2     c 0 1 1
5:  2     b 0 1 1

编辑您仍然可以使用与<code>dcast

我注意到您的“从这个开始”数据的第2行与“想要这个”数据的接收日期不同,您只保留了“1-1-99”。

DT2 <- data.table::data.table(ID=c("1","1","1","2","2"),
                             CLASS=c("a","a","b","c","b"),
                             a=c(0,0,0,0,0),
                             b=c(0,0,0,0,0),
                             c=c(0,0,0,0,0), 
                             RXDate = c("1-1-99", "1-2-99", "1-3-99", "5-4-00", "6-5-01"))

# 2nd row from the data provided has different RXDate under same ID and Class.
# Use x[1] to pick first
DT_dcast <- dcast(DT2[, .(ID, CLASS, RXDate)], ID ~ CLASS, 
                  fun.aggregate = function(x) x[1], 
                  value.var = c("CLASS", "RXDate"))
DT_dcast
   ID CLASS.1_a CLASS.1_b CLASS.1_c RXDate_a RXDate_b RXDate_c
1:  1         a         b      <NA>   1-1-99   1-3-99     <NA>
2:  2      <NA>         b         c     <NA>   6-5-01   5-4-00

# Convert 1 or 0 under CLASS
class_cols <- names(DT_dcast)[grepl("CLASS", names(DT_dcast))]
for (col in class_cols) set(DT_dcast, j = col, value = ifelse(is.na(DT_dcast[[col]]), 0, 1))

DT_dcast
ID CLASS.1_a CLASS.1_b CLASS.1_c RXDate_a RXDate_b RXDate_c
1:  1         1         1         0   1-1-99   1-3-99     <NA>
2:  2         0         1         1     <NA>   6-5-01   5-4-00

# Then merge with the original data.table
DT_m <- merge(DT2[, .(ID, CLASS, RXDate)], DT_dcast, by = "ID")
DT_m

   ID CLASS RXDate CLASS.1_a CLASS.1_b CLASS.1_c RXDate_a RXDate_b RXDate_c
1:  1     a 1-1-99         1         1         0   1-1-99   1-3-99     <NA>
2:  1     a 1-2-99         1         1         0   1-1-99   1-3-99     <NA>
3:  1     b 1-3-99         1         1         0   1-1-99   1-3-99     <NA>
4:  2     c 5-4-00         0         1         1     <NA>   6-5-01   5-4-00
5:  2     b 6-5-01         0         1         1     <NA>   6-5-01   5-4-00

如果您想重命名列,那么您可以使用setname来完成

 类似资料:
  • 我有一个自定义类数据列表,我想根据一个字段和值应该是另一个字段对它们进行分组。以下面的例子为例。 现在我想基于类对这些数据进行分组。预期的输出应该是一个映射,其中包含作为类的键和作为学生姓名列表的值。 我的代码是这样的:

  • 给定一个包含几列的dataFrame,我试图创建一个新列,其中包含根据这些列的行值按递减顺序排序的这些列名称的数组。 列的名称存储在var名称中:数组[字符串] 我应该采取什么方法?

  • 问题内容: 例如: 但是上述方法不起作用。我正在使用MySql 5.x 问题答案: 可以在一个语句中进行多个表更改,但一次只能处理一列,因此您需要为要更改的每一列指定: 另外,请注意手册中的以下警告: 当您使用CHANGE或MODIFY时,必须包括数据类型和应应用于新列的所有属性,而索引属性(例如PRIMARY KEY或UNIQUE)除外。原始定义中存在但未为新定义指定的属性不会继续。

  • 问题内容: 我有以下独特的约束 我正在尝试防止两个问题处于活动状态时具有相同的问题编号(IS_ACTIVE值= 1)。 一切似乎都很好,直到我第二次不得不提一个问题。 我需要仅在IS_ACTIVE = 1时应用约束 问题答案: 您可以创建一个基于函数的唯一索引 这利用了以下事实:Oracle b树索引不存储数据,而叶块数据将完全为NULL。

  • 这是我的JPA实体类: 这个类有一个相当复杂的唯一约束:的组合被认为是唯一的。 所以我在类中添加了以下约束注释: 当我尝试练习我的课时,我得到以下例外情况: 异常要点是: 如果我没有在幕后为单个实体类指定该约束,Hibernate将生成两个表:一个表用于所有“简单”实体数据,另一个表用于实体数据: ... 这个问题以前读: JPA:跨集的唯一约束(...另一个表上的唯一约束) 进一步思考我的问题,

  • 我定义了一个配置,其中包含每个表的列列表,用作dedup键 例如:配置1: 这些列需要用作重复数据消除键。这个列表是动态的,有些表中有1个值,有些表中有2或3个值 我想做的是从这个列表中建立一个键列 我如何使这个动态的列表适用于任意数量的列。我试过这么做 为了实现这一点,我必须将列表转换为Df,列表中的每个值都需要在单独的列中,我无法理解这一点。 试过这么做但没用 欢迎任何意见,谢谢