我有一个看起来像这样的数据框,并延伸了数百万行:
id class weight
1: 3930271 77 1.0
2: 3930272 55 0.5
3: 3930272 654 0.5
4: 3930273 66 0.5
5: 3930273 66 0.5
6: 3930274 225 1.0
7: 3930275 66 0.05
7: 3930275 44 0.05
...
...
34'000'000:
那是因为每个类在同一个ID中出现多次。权重栏衡量专利的每个类别的分数值(拥有2个类别的专利意味着每个类别贡献0.5)。 现在,我想通过在同一行中简单地包含一个patent_id和多个类(从1到最大20)来减少行数。我想要的是这样的:
id class1 class2 ... class20 weight
1: 3930271 77 0 0 1
2: 3930272 55 654 0 0.5
3: 3930273 65 66 0 0.5
4: 3930274 225 0 0 1
5: 3930275 66 44 30 0.05
6: 3930276 225 33 0 0.5
某些id不会有20个类,因此在这种情况下,它应该返回0或点。当类具有超过20个值时,没有选择标准的条件,因为与数百万个id相比,它仅发生几次。 一些patent_id将具有20多个类,但是我想排除那些(很少观察到)。 你会怎么做? 我尝试了tidyr的功能传播,但报告了此错误消息
Error: Each row of output must be identified by a unique combination of keys.
那是因为有时class的值会重复出现,但是我需要保持原样。
答案 0 :(得分:1)
可能有一种更清洁的方式来完成此任务。阅读tidyr :: spread()和collect()以及data.table :: dcast(),melt()和cast()。
使用您提供的示例数据:
sample_data <- data.frame("id" = c(3930271, 3930272, 3930272,
3930273, 3930273, 3930274,
3930275, 3930275),
"class" = c(77, 55, 654,
66, 66, 225,
66, 44),
"weight" = c(1, 0.5, 0.5, 0.5,
0.5, 1, 0.05, 0.05))
使用dplyr的summary函数汇总每个id的类值。这将进行过滤,以排除具有超过20个唯一类值的所有ID。
library(dplyr) #imports the group_by, summarize, filter, select, and bind_cols functions
library(magrittr) #imports the %>% pipe function
library(tidyr) #imports the separate function
wide_df_new <- sample_data %>%
group_by(id) %>%
summarize(class_list = list(class),
n = n(),
weight = 1/n) %>%
filter(n <= 20) %>%
select(-n)
创建新列名的向量:
new_col_names <- paste0("class", 1:20)
每个id在数据框sample_data$class_list
的单个列中都有其类的列表(列表中的列表)。将此单个列表拆分为新列。
wide_df_new <- separate(wide_df_new , col = class_list,
into = new_col_names,
remove = TRUE,
sep = ", ")
从列中删除字符向量工件。这将从所有列中删除所有非数字字符!转换回数字并绑定列以返回到数据框数据结构。
wide_df_new <- lapply(wide_df_new , function(x) gsub("[^0-9\\.]", "", x)) %>%
lapply(as.numeric) %>%
bind_cols()
最后但并非最不重要的一点是,用0填充所有NA值。
wide_df_new[is.na(wide_df_new)] <- 0
就像我最初说的那样,可能有一种更清洁的方式来做到这一点。
答案 1 :(得分:0)
这是一个data.table
答案,应该可以为您提供所需的输出。诀窍是使用.N
中的特殊符号data.table
获得编号,这将以动态方式为类创建所需的序列。这是示例数据,请注意我将其转换为data.table
:
library(data.table)
sample_data <- data.table("id" = c(3930271, 3930272, 3930272,
3930273, 3930273, 3930274,
3930275, 3930275),
"class" = c(77, 55, 654,
65, 66, 225,
66, 44),
"weight" = c(1, 0.5, 0.5, 0.5,
0.5, 1, 0.05, 0.05))
编辑:我刚刚意识到您只希望ID的前20个出现,因此将其添加到我的答案中。或者,您是否要删除ID出现次数超过20次的所有实例?请澄清。
melt_dt<-sample_data[,melt(.SD,measure.vars=c("class"))]
melt_dt[,id_count:=seq_len(.N),by="id"][id_count<=20][,colname_val:=paste0("class",id_count)]
wide_dt<-dcast(melt_dt,id+weight~colname_val, value.var="value",fill=0)
因此,首先只需融化并指定“ class”作为您的测量变量。然后创建一个变量,该变量将计算相同ID的数量。如果只希望出现的前20个,则仅选择少于20个的行,如melted_dt
的第二个链所示。最后,将这些数字附加到“类”中,以在名为colname_val
的变量中获得所需的命名方案。最后,您可以使用data.table
创建宽形dcast
并添加fill=0
参数来替换NAs
。这有帮助吗?让我知道您是否需要我澄清任何事情。祝你好运!
答案 2 :(得分:0)
使用data.table的另一种方法
library(data.table)
sample_data[, ri := paste0("class",seq_len(.N)), by=.(id, weight)]
ans <- dcast(sample_data[ri<=20], id + weight ~ ri, value.var="class")
ans[, names(ans) := lapply(.SD, function(x) replace(x, is.na(x), 0))]
ans