[英]fast way to separate list of list into two lists
我在C編程方面有很好的經驗,並且習慣於使用指針進行思考,因此在處理大量數據時可以獲得良好的性能。 R與R並不相同,我仍在學習。
我有一個大約有100萬行的文件,以'\\ n'分隔,每行內部有1、2個或多個整數,以''分隔。 我已經能夠編寫一個讀取文件並將所有內容放入列表列表的代碼。 有些行可以為空。 然后,我想將每行的第一個數字(如果存在的話)放入一個單獨的列表中,如果某行為空,則將其過去,然后將剩余的數字放入第二個列表中。
我在這里發布的代碼非常慢 (自從我開始寫這個問題以來,它一直在運行,所以現在我殺了R),如何獲得一個不錯的速度? 在C語言中,這將立即完成。
graph <- function() {
x <- scan("result", what="", sep="\n")
y <- strsplit(x, "[[:space:]]+") #use spaces for split number in each line
y <- lapply(y, FUN = as.integer) #convert from a list of lists of characters to a list of lists of integers
print("here we go")
first <- c()
others <- c()
for(i in 1:length(y)) {
if(length(y[i]) >= 1) {
first[i] <- y[i][1]
}
k <- 2;
for(j in 2:length(y[i])) {
others[k] <- y[i][k]
k <- k + 1
}
}
在以前的代碼版本中,每行至少有一個數字,而我只對每行的第一個數字感興趣,因此我使用了這段代碼(我讀了很多書,應該避免在諸如R)
yy <- rapply(y, function(x) head(x,1))
這大約需要5秒鍾,到目前為止遠遠超過了上面的時間,但是與C相比仍然很煩人 。
編輯這是我文件前10行的示例:
42 7 31 3
23 1 34 5
1
-23 -34 2 2
42 7 31 3 31 4
1
基數R與Purrr
your_list <- rep(list(list(1,2,3,4), list(5,6,7), list(8,9)), 100)
microbenchmark::microbenchmark(
your_list %>% map(1),
lapply(your_list, function(x) x[[1]])
)
Unit: microseconds
expr min lq mean median uq max neval
your_list %>% map(1) 22671.198 23971.213 24801.5961 24775.258 25460.4430 28622.492 100
lapply(your_list, function(x) x[[1]]) 143.692 156.273 178.4826 162.233 172.1655 1089.939 100
microbenchmark::microbenchmark(
your_list %>% map(. %>% .[-1]),
lapply(your_list, function(x) x[-1])
)
Unit: microseconds
expr min lq mean median uq max neval
your_list %>% map(. %>% .[-1]) 916.118 942.4405 1019.0138 967.4370 997.2350 2840.066 100
lapply(your_list, function(x) x[-1]) 202.956 219.3455 264.3368 227.9535 243.8455 1831.244 100
purrr並不是性能套件,只是方便,這很棒,但是當您非常在意性能時卻不是。 這已經在其他地方討論過了。
順便說一句,如果您精通C語言,則應查看軟件包Rcpp 。
嘗試這個:
your_list <- list(list(1,2,3,4),
list(5,6,7),
list(8,9))
library(purrr)
first <- your_list %>% map(1)
# [[1]]
# [1] 1
#
# [[2]]
# [1] 5
#
# [[3]]
# [1] 8
other <- your_list %>% map(. %>% .[-1])
# [[1]]
# [[1]][[1]]
# [1] 2
#
# [[1]][[2]]
# [1] 3
#
# [[1]][[3]]
# [1] 4
#
#
# [[2]]
# [[2]][[1]]
# [1] 6
#
# [[2]][[2]]
# [1] 7
#
#
# [[3]]
# [[3]][[1]]
# [1] 9
盡管您可能需要以下內容,但在我看來,將這些數字更好地存儲在矢量中而不是列表中:
your_list %>% map(1) %>% unlist # as it seems map_dbl was slow
# [1] 1 5 8
your_list %>% map(~unlist(.x[-1]))
# [[1]]
# [1] 2 3 4
#
# [[2]]
# [1] 6 7
#
# [[3]]
# [1] 9
確實,從C到R會造成混亂(這是對我而言)。 有助於提高性能的方法是,了解R中的原始類型都是在高度優化的,本機編譯的C和Fortran中實現的所有向量 ,並且當有矢量化解決方案可用時,您應力爭避免循環。
就是說,我認為您應該通過read.csv()
其作為csv加載。 這將為您提供一個數據框,您可以使用該數據框執行基於矢量的操作。
為了獲得更好的理解,請訪問http://www.burns-stat.com/pages/Tutor/R_inferno.pdf進行簡潔(幽默)的閱讀。
我會嘗試使用stringr
包。 像這樣:
set.seed(3)
d <- replicate(3, sample(1:1000, 3))
d <- apply(d, 2, function(x) paste(c(x, "\n"), collapse = " "))
d
# [1] "169 807 385 \n" "328 602 604 \n" "125 295 577 \n"
require(stringr)
str_split(d, " ", simplify = T)
# [,1] [,2] [,3] [,4]
# [1,] "169" "807" "385" "\n"
# [2,] "328" "602" "604" "\n"
# [3,] "125" "295" "577" "\n"
即使是大數據,它也很快:
d <- replicate(1e6, sample(1:1000, 3))
d <- apply(d, 2, function(x) paste(c(x, "\n"), collapse = " "))
d
system.time(s <- str_split(d, " ", simplify = T)) #0.77 sek
假設文件采用CSV格式,並且所有“數字”嚴格采用1 2
或-1 2
的格式( 即文件中不允許使用1 2 3
或1 23
),那么可以通過編碼開始:
# Install package `data.table` if needed
# install.packages('data.table')
# Load `data.table` package
library(data.table)
# Load the CSV, which has just one column named `my_number`.
# Then, coerce `my_number` into character format and remove negative signs.
DT <- fread('file.csv')[, my_number := as.character(abs(my_number))]
# Extract first character, which would be the first desired digit
# if my assumption about number formats is correct.
DT[, first_column := substr(my_number, 1, 1)]
# The rest of the substring can go into another column.
DT[, second_column := substr(my_number, 2, nchar(my_number))].
然后,如果您仍然確實需要創建兩個列表,則可以執行以下操作。
# Create the first list.
first_list <- DT[, as.list(first_column)]
# Create the second list.
second_list <- DT[, as.list(second_column)]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.