我有一个data.frame,我希望按行转换为列表,这意味着每一行都对应于它自己的列表元素。换句话说,我想要一个只要data.frame有行的列表。
到目前为止,我已经通过以下方式解决了这个问题,但我想知道是否有更好的方法来解决这个问题。
xy.df <- data.frame(x = runif(10), y = runif(10))
# pre-allocate a list and fill it with a loop
xy.list <- vector("list", nrow(xy.df))
for (i in 1:nrow(xy.df)) {
xy.list[[i]] <- xy.df[i,]
}
答案 0 :(得分:125)
像这样:
xy.list <- split(xy.df, seq(nrow(xy.df)))
如果您希望xy.df
的rownames成为输出列表的名称,则可以执行以下操作:
xy.list <- setNames(split(xy.df, seq(nrow(xy.df))), rownames(xy.df))
答案 1 :(得分:45)
尤里卡!
xy.list <- as.list(as.data.frame(t(xy.df)))
答案 2 :(得分:13)
如果你想完全滥用data.frame(就像我一样)并且喜欢保留$功能,一种方法是将data.frame分成一行收集在列表中的数据。
> df = data.frame(x=c('a','b','c'), y=3:1)
> df
x y
1 a 3
2 b 2
3 c 1
# 'convert' into a list of data.frames
ldf = lapply(as.list(1:dim(df)[1]), function(x) df[x[1],])
> ldf
[[1]]
x y
1 a 3
[[2]]
x y
2 b 2
[[3]]
x y
3 c 1
# and the 'coolest'
> ldf[[2]]$y
[1] 2
它不仅是智力手淫,而且允许将data.frame“转换”为其行列表,保留$ indexation,这对于lapply的进一步使用是有用的(假设你传递给lapply的函数使用这个$ indexation)
答案 3 :(得分:7)
我今天正在研究一个包含数百万个观测值和35列的data.frame(实际上是一个data.table)。我的目标是返回一个data.frames(data.tables)列表,每个列都有一行。也就是说,我想将每一行拆分成一个单独的data.frame并将它们存储在一个列表中。
我提出的两种方法比该数据集的split(dat, seq_len(nrow(dat)))
大约快3倍。下面,我对7500行,5列数据集( iris 重复50次)的三种方法进行基准测试。
library(data.table)
library(microbenchmark)
microbenchmark(
split={dat1 <- split(dat, seq_len(nrow(dat)))},
setDF={dat2 <- lapply(seq_len(nrow(dat)),
function(i) setDF(lapply(dat, "[", i)))},
attrDT={dat3 <- lapply(seq_len(nrow(dat)),
function(i) {
tmp <- lapply(dat, "[", i)
attr(tmp, "class") <- c("data.table", "data.frame")
setDF(tmp)
})},
datList = {datL <- lapply(seq_len(nrow(dat)),
function(i) lapply(dat, "[", i))},
times=20
)
返回
Unit: milliseconds
expr min lq mean median uq max neval
split 861.8126 889.1849 973.5294 943.2288 1041.7206 1250.6150 20
setDF 459.0577 466.3432 511.2656 482.1943 500.6958 750.6635 20
attrDT 399.1999 409.6316 461.6454 422.5436 490.5620 717.6355 20
datList 192.1175 201.9896 241.4726 208.4535 246.4299 411.2097 20
虽然差异不像我之前的测试那么大,但是直接setDF
方法在运行分布的所有级别上都明显更快,其中max(setDF)&lt; min(split)和attr
方法通常快两倍以上。
第四种方法是极端冠军,它是一个简单的嵌套lapply
,返回一个嵌套列表。此方法举例说明了从列表构造data.frame的成本。此外,我使用data.frame
函数尝试的所有方法都比data.table
技术慢了一个数量级。
数据强>
dat <- vector("list", 50)
for(i in 1:50) dat[[i]] <- iris
dat <- setDF(rbindlist(dat))
答案 4 :(得分:6)
似乎reg.exe query HKCU\Environment
(0.2.2)包的当前版本是最快的解决方案:
purrr
让我们比较最有趣的解决方案:
by_row(x, function(v) list(v)[[1L]], .collate = "list")$.out
Rsults:
data("Batting", package = "Lahman")
x <- Batting[1:10000, 1:10]
library(benchr)
library(purrr)
benchmark(
split = split(x, seq_len(.row_names_info(x, 2L))),
mapply = .mapply(function(...) structure(list(...), class = "data.frame", row.names = 1L), x, NULL),
purrr = by_row(x, function(v) list(v)[[1L]], .collate = "list")$.out
)
我们也可以使用Benchmark summary:
Time units : milliseconds
expr n.eval min lw.qu median mean up.qu max total relative
split 100 983.0 1060.0 1130.0 1130.0 1180.0 1450 113000 34.3
mapply 100 826.0 894.0 963.0 972.0 1030.0 1320 97200 29.3
purrr 100 24.1 28.6 32.9 44.9 40.5 183 4490 1.0
获得相同的结果:
Rcpp
现在与#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
List df2list(const DataFrame& x) {
std::size_t nrows = x.rows();
std::size_t ncols = x.cols();
CharacterVector nms = x.names();
List res(no_init(nrows));
for (std::size_t i = 0; i < nrows; ++i) {
List tmp(no_init(ncols));
for (std::size_t j = 0; j < ncols; ++j) {
switch(TYPEOF(x[j])) {
case INTSXP: {
if (Rf_isFactor(x[j])) {
IntegerVector t = as<IntegerVector>(x[j]);
RObject t2 = wrap(t[i]);
t2.attr("class") = "factor";
t2.attr("levels") = t.attr("levels");
tmp[j] = t2;
} else {
tmp[j] = as<IntegerVector>(x[j])[i];
}
break;
}
case LGLSXP: {
tmp[j] = as<LogicalVector>(x[j])[i];
break;
}
case CPLXSXP: {
tmp[j] = as<ComplexVector>(x[j])[i];
break;
}
case REALSXP: {
tmp[j] = as<NumericVector>(x[j])[i];
break;
}
case STRSXP: {
tmp[j] = as<std::string>(as<CharacterVector>(x[j])[i]);
break;
}
default: stop("Unsupported type '%s'.", type2name(x));
}
}
tmp.attr("class") = "data.frame";
tmp.attr("row.names") = 1;
tmp.attr("names") = nms;
res[i] = tmp;
}
res.attr("names") = x.attr("row.names");
return res;
}
:
purrr
结果:
benchmark(
purrr = by_row(x, function(v) list(v)[[1L]], .collate = "list")$.out,
rcpp = df2list(x)
)
答案 5 :(得分:3)
更现代的解决方案仅使用purrr::transpose
:
library(purrr)
iris[1:2,] %>% purrr::transpose()
#> [[1]]
#> [[1]]$Sepal.Length
#> [1] 5.1
#>
#> [[1]]$Sepal.Width
#> [1] 3.5
#>
#> [[1]]$Petal.Length
#> [1] 1.4
#>
#> [[1]]$Petal.Width
#> [1] 0.2
#>
#> [[1]]$Species
#> [1] 1
#>
#>
#> [[2]]
#> [[2]]$Sepal.Length
#> [1] 4.9
#>
#> [[2]]$Sepal.Width
#> [1] 3
#>
#> [[2]]$Petal.Length
#> [1] 1.4
#>
#> [[2]]$Petal.Width
#> [1] 0.2
#>
#> [[2]]$Species
#> [1] 1
答案 6 :(得分:2)
使用library(purrr)
的另一种替代方法(在大型数据框架上似乎更快一点)
flatten(by_row(xy.df, ..f = function(x) flatten_chr(x), .labels = FALSE))
答案 7 :(得分:2)
对我来说最好的方法是:
示例数据:
Var1<-c("X1",X2","X3")
Var2<-c("X1",X2","X3")
Var3<-c("X1",X2","X3")
Data<-cbind(Var1,Var2,Var3)
ID Var1 Var2 Var3
1 X1 X2 X3
2 X4 X5 X6
3 X7 X8 X9
我们称之为BBmisc
库
library(BBmisc)
data$lists<-convertRowsToList(data[,2:4])
结果将是:
ID Var1 Var2 Var3 lists
1 X1 X2 X3 list("X1", "X2", X3")
2 X4 X5 X6 list("X4","X5", "X6")
3 X7 X8 X9 list("X7,"X8,"X9)
答案 8 :(得分:1)
另一种方法是将df转换为矩阵,然后应用列表应用lappy
函数:ldf <- lapply(as.matrix(myDF), function(x)x)
答案 9 :(得分:1)
更多选择:
使用asplit
asplit(xy.df, 1)
#[[1]]
# x y
#0.1137 0.6936
#[[2]]
# x y
#0.6223 0.5450
#[[3]]
# x y
#0.6093 0.2827
#....
使用split
和row
split(xy.df, row(xy.df)[, 1])
#$`1`
# x y
#1 0.1137 0.6936
#$`2`
# x y
#2 0.6223 0.545
#$`3`
# x y
#3 0.6093 0.2827
#....
数据
set.seed(1234)
xy.df <- data.frame(x = runif(10), y = runif(10))
答案 10 :(得分:0)
by_row
包中的purrrlyr
函数会为您执行此操作。
此示例演示
myfn <- function(row) {
#row is a tibble with one row, and the same number of columns as the original df
l <- as.list(row)
return(l)
}
list_of_lists <- purrrlyr::by_row(df, myfn, .labels=FALSE)$.out
默认情况下,myfn
的返回值会被放入名为.out
的df中的新list column。上述语句末尾的$.out
会立即选中此列,并返回列表列表。
答案 11 :(得分:0)
喜欢@flodel写道: 这会将您的数据帧转换为一个列表,该列表具有与dataframe中的行数相同的元素数量:
val someList = Seq(1,2,3,4,5)
//Prints every element of the list
someList.foreach(println(_))
您可以另外添加一个功能仅在列表的每个元素中选择那些非NA 的列:
NewList <- split(df, f = seq(nrow(df)))