Простой способ сделать это - создать факторный вектор, добавив строку sys
к номерам идентификаторов и используя ее для разделения данных.Нет необходимости использовать цикл for()
для получения желаемого результата, поскольку результатом split()
является список фреймов данных, когда вход, который нужно разделить, является фреймом данных.
Значение коэффициента используется для именования каждого элемента в списке, сгенерированном split()
.В случае OP, поскольку sysid
является числовым и начинается с 1, не очевидно, что номера идентификаторов используются для именования результирующих кадров данных в списке, как объяснено в справке для 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()
...
Поскольку ОП спросил, можно ли решить проблему с помощью цикла 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 микросекунд против 305 микросекунд для by()
и колоссальные 3458 микросекунд для циклического подхода for()
.