在R中使用“ for循环”将一个数据帧拆分为几个数据帧

时间:2018-11-21 11:04:25

标签: r for-loop

我对for循环有疑问。 我有一个具有120个唯一ID的数据框。我想根据ID将数据框分为120个不同的数据框。我使用以下代码对其进行了拆分:

split_part0 <- split(PART0_DF, PART0_DF$sysid)

现在我想做

for(i in 1:120){ 
sys[i] <- as.data.frame(split_part0[[i]])}

这样,我有120个数据帧,它们具有唯一的帧名称,可用于进一步分析。 在这种特殊情况下无法使用“ for循环”吗?如果是这样,我还可以使用哪些其他命令? PART0_DF的虚拟数据:

 Date      sysid   power   temperature
 1.1.2018    1     1000       14
 2.1.2018    1     1200       16
 3.1.2018    1      800       18
 1.1.2018    2     1500        8
 2.1.2018    2      800       18
 3.1.2018    2     1300       11

我希望输出像

     >>sys1
     Date      sysid   power   temperature
     1.1.2018    1     1000     14
     2.1.2018    1     1200     16
     3.1.2018    1      800     18
     >>sys2
     1.1.2018    2     1500      8
     2.1.2018    2      800     18
     3.1.2018    2     1300     11

2 个答案:

答案 0 :(得分:0)

一种简单的方法是通过将字符串sys附加到ID号上并使用它来分割数据来创建因子向量。不需要使用for()循环来生成所需的输出,因为当要拆分的输入是数据帧时,split()的结果是数据帧的列表。

因子的值用于命名split()生成的列表中的每个元素。对于OP,由于sysid是数字,并且以1开头,所以不明显使用ID号来命名列表中的结果数据帧,如help for split()中所述。

使用来自OP的数据,我们将说明如何使用sysid列创建因子变量,该变量将字符串sys与id值组合在一起,并将其拆分为数据列表可以通过名称访问的框架。

rawData <- "Date      sysid   power   temperature
 1.1.2018    1     1000       14
 2.1.2018    1     1200       16
 3.1.2018    1      800       18
 1.1.2018    2     1500        8
 2.1.2018    2      800       18
 3.1.2018    2     1300       11"

data <- read.table(text = rawData,header=TRUE)
sysidName <- paste0("sys",data$sysid)

splitData <- split(data,sysidName)

splitData

...以及输出:

> splitData
$`sys1`
      Date sysid power temperature
1 1.1.2018     1  1000          14
2 2.1.2018     1  1200          16
3 3.1.2018     1   800          18

$sys2
      Date sysid power temperature
4 1.1.2018     2  1500           8
5 2.1.2018     2   800          18
6 3.1.2018     2  1300          11

>

这时,可以使用提取操作符的$形式访问列表中的各个数据帧:

> splitData$sys1
      Date sysid power temperature sysidName
1 1.1.2018     1  1000          14      sys1
2 2.1.2018     1  1200          16      sys1
3 3.1.2018     1   800          18      sys1
>

此外,通过使用names()函数,可以获取数据帧列表中所有命名元素的向量。

> names(splitData)
[1] "sys1" "sys2"
> 

从答案的顶部重申要点,当split()与数据帧一起使用时,结果列表为data.frame()类型的对象的列表。例如:

> str(splitData["sys1"])
List of 1
 $ sys1:'data.frame':   3 obs. of  4 variables:
  ..$ Date       : Factor w/ 3 levels "1.1.2018","2.1.2018",..: 1 2 3
  ..$ sysid      : int [1:3] 1 1 1
  ..$ power      : int [1:3] 1000 1200 800
  ..$ temperature: int [1:3] 14 16 18
>

如果必须使用for()循环...

由于OP询问是否可以使用for()循环解决问题,所以答案是“是”。

# create a vector containing unique values of sysid
ids <- unique(data$sysid)
# initialize output data frame list 
dfList <- list() 
# loop thru unique values and generate named data frames in list() 
for(i in ids){
     dfname <- paste0("sys",i)
     dfList[[dfname]] <- data[data$sysid == i,]
}
dfList 

...以及输出:

> for(i in ids){
+      dfname <- paste0("sys",i)
+      dfList[[dfname]] <- data[data$sysid == i,]
+ }
> dfList
$`sys1`
      Date sysid power temperature
1 1.1.2018     1  1000          14
2 2.1.2018     1  1200          16
3 3.1.2018     1   800          18

$sys2
      Date sysid power temperature
4 1.1.2018     2  1500           8
5 2.1.2018     2   800          18
6 3.1.2018     2  1300          11

选择“最佳”答案

split()for()和使用by()的其他答案之间,我们如何选择最佳答案?

一种方法是确定哪个版本运行最快,因为实际数据将比原始帖子中的样本数据大得多。

我们可以使用microbenchmark包来比较三种不同方法的性能。

split()性能

library(microbenchmark)
> microbenchmark(splitData <- split(data,sysidName),unit="us")
Unit: microseconds
                                expr     min      lq     mean   median       uq     max neval
 splitData <- split(data, sysidName) 144.594 147.359 185.7987 150.1245 170.4705 615.507   100
> 

for()性能

> microbenchmark(for(i in ids){
+      dfname <- paste0("sys",i)
+      dfList[[dfname]] <- data[data$sysid == i,]
+ },unit="us")
Unit: microseconds
                                                                                              expr      min       lq     mean
 for (i in ids) {     dfname <- paste0("sys", i)     dfList[[dfname]] <- data[data$sysid == i, ] } 2643.755 2857.286 3457.642
   median       uq      max neval
 3099.064 3479.311 8511.609   100
>

by()性能

> microbenchmark(df_list <- by(df, df$sysid, function(unique) unique),unit="us")
Unit: microseconds
                                                 expr     min       lq     mean   median       uq      max neval
 df_list <- by(df, df$sysid, function(unique) unique) 256.791 260.5445 304.9296 275.9515 309.5325 1218.372   100
>

...获胜者是:

split(),平均运行时间为186微秒,而by()为305微秒,而for()循环方法为3,458微秒。

答案 1 :(得分:0)

另一个选择是使用函数by()

df <- data.frame(
  Date = c("1.1.2018",  "2.1.2018", "3.1.2018", "1.1.2018", "2.1.2018", "3.1.2018"),
  sysid = c(1, 1, 1, 2, 2, 2),
  power = c(1000, 1200, 800, 1500, 800, 1300)
  )
df
  Date sysid power
1 1.1.2018     1  1000
2 2.1.2018     1  1200
3 3.1.2018     1   800
4 1.1.2018     2  1500
5 2.1.2018     2   800
6 3.1.2018     2  1300

现在使用df并调用sysid,将by()分成尽可能多的数据帧,只要它们具有不同的(“唯一的”)unique值:

df_list <- by(df, df$sysid, function(unique) unique)
df_list
df$sysid: 1
      Date sysid power
1 1.1.2018     1  1000
2 2.1.2018     1  1200
3 3.1.2018     1   800
---------------------------------------------------------------------------------------------- 
df$sysid: 2
      Date sysid power
4 1.1.2018     2  1500
5 2.1.2018     2   800
6 3.1.2018     2  1300