我在R中获得了一个数据框,其中一个字段是复合的(分隔的)。这是我得到的一个例子:
users=c(1,2,3)
items=c("23 77 49", "10 18 28", "20 31 84")
df = data.frame(users,items)
(我不构建它;这仅用于说明目的。)
users items
1 23 77 49
2 10 18 28
3 20 31 84
我想展平第二列,以便获得(非唯一的)用户ID列表和每行单独的项目。所以我想最终:
user item
1 23
1 77
1 49
2 10
2 18
2 28
3 20
3 31
3 84
我试过了:
data.frame(user = df$users, item = unlist(strsplit(as.character(df$items), " ")))
但我得到“论据意味着不同的行数”。我理解为什么,但找不到解决办法给我想要的结果。有什么想法吗?
另外,当我获得超过2000万行时,最有效的方法是什么?
答案 0 :(得分:2)
items <- strsplit(df$items, " ")
data.frame(user = rep(df$users, sapply(items, length)), item = unlist(items))
## user item
## 1 1 23
## 2 1 77
## 3 1 49
## 4 2 10
## 5 2 18
## 6 2 28
## 7 3 20
## 8 3 31
## 9 3 84
或
library(data.table)
DT <- data.table(df)
DT[, list(item = unlist(strsplit(items, " "))), by = users]
## users item
## 1: 1 23
## 2: 1 77
## 3: 1 49
## 4: 2 10
## 5: 2 18
## 6: 2 28
## 7: 3 20
## 8: 3 31
## 9: 3 84
答案 1 :(得分:1)
这是dplyr
解决方案
users=c(1,2,3)
items=c("23 77 49", "10 18 28", "20 31 84")
df = data.frame(users,items,stringsAsFactors=FALSE)
rbind_all(do(df %.% group_by(users),
.f = function(d) data.frame(d[,1,drop=FALSE],
items = unlist(strsplit(d[['items']],' ')),
stringsAsFactors=FALSE)))
拥有expand
函数会非常好,即与summarise
相反
例如。如果以下可行。
df %.% group_by(users) %.% expand(unlist(strsplit(items,' ')))
答案 2 :(得分:1)
如果您愿意安装我的“SOfun”软件包或加载我的concat.split.DT
function,并且如果每个“item”字符串中的项目数相同(在您的示例中,有3个),则以下可能是一个选项:
library(reshape2)
library(data.table)
melt(concat.split.DT(indf, "items", " "), id.vars="users")
这是一个例子。
我添加了一个“id”列,以便您可以比较两个选项的输出。
## your sample data.frame
df <- data.frame(users=c(1,2,3),
items=c("23 77 49", "10 18 28", "20 31 84"))
## extended to 3000 rows
df1k <- df[rep(rownames(df), 1000), ]
df1k$id <- sequence(nrow(df1k))
## extended to 3 million rows
df1m <- df1M <- df[rep(rownames(df), 1000000), ]
df1m$id <- sequence(nrow(df1m))
concat.split.DT
使用“data.table”中的fread
来分割连接值。 melt
# library(devtools)
# install_github("SOfun", "mrdwab")
library(SOfun)
library(data.table)
library(reshape2)
packageVersion("data.table")
# [1] ‘1.8.11’
以下是测试Jake答案速度的一些功能。稍后我会尝试用“dplyr”更新。
fun1 <- function(indf) {
DT <- melt(concat.split.DT(indf, "items", " "),
id.vars=c("id", "users"))
setkeyv(DT, c("id", "users"))
DT
}
fun2 <- function(indf) {
DT <- data.table(indf)
DT[, list(item = unlist(strsplit(as.character(items), " "))),
by = list(id, users)]
}
在3,000行进行测试
microbenchmark(fun1(df1k), fun2(df1k))
# Unit: milliseconds
# expr min lq median uq max neval
# fun1(df1k) 17.64675 18.21658 18.79859 21.21943 71.7737 100
# fun2(df1k) 152.97974 158.44148 163.12707 199.77297 345.7508 100
在3,000,000行上测试(仅一次)
时间会在几秒钟内......
system.time(fun1(df1m))
# user system elapsed
# 7.71 0.94 8.69
system.time(fun2(df1m))
# user system elapsed
# 177.80 0.50 178.97
data.table
方法的输出,看看结果是一样的。
删除“id”列并删除fun1
和fun2
中对“id”的引用,为我们提供以下内容:
microbenchmark(fun1a(df1M), fun2a(df1M), fun3(df1M), times = 5)
# Unit: seconds
# expr min lq median uq max neval
# fun1a(df1M) 2.307313 2.420845 2.630284 2.822011 3.074464 5
# fun2a(df1M) 12.480502 12.491783 12.761392 13.069169 13.733686 5
# fun3(df1M) 13.976329 14.281856 14.471252 15.041450 15.089593 5
上面的基准测试是fun3
,这是@ mnel的“dplyr”方法。
fun3 <- function(indf) {
rbind_all(do(indf %.% group_by(users),
.f = function(d) data.frame(
d[,1,drop=FALSE],
items = unlist(strsplit(as.character(d[['items']]),' ')),
stringsAsFactors=FALSE)))
}
非常好的表现所有答案!