我有几个这样的CSV文件:
site,run,id,payload,dir
1,1,1,528,1
1,1,1,540,2
1,1,3,532,1
# ... thousands more rows ...
(在我正在使用的实际情况中,有三个文件,总共有1,408,378行。)对于绘图,我想将它们重新调整为这种格式:
label,stream,dir,i,payload
A,1,1,1,586
A,1,1,2,586
A,1,1,3,586
# etc
其中'label'源自CSV文件的名称; 'stream'是在一个文件中分配给'site','run'和'id'的每个组合的序列号(因此,仅在'label'中唯一); 'i'是每个'stream'中的行号; 'dir'和'payload'直接取自原始文件。我还想丢弃除了每个流的前20行之外的所有行。我事先知道CSV文件中的每个单元格(标题除外)都是正整数,而'dir'只取值1和2.
我用plyr
杀死了我最初尝试这样做的原因,因为经过一个多小时的计算后,它已经运行了R进程,最多可达6GB的工作集,看不到任何结束。最新foreach
中对plyr
并行性的闪亮新支持没有帮助:八个进程分别运行10分钟的CPU时间,然后又回到一个进程,持续一个小时,然后,是的,再次吹掉我的RAM。
然后我在Python中编写了一个帮助脚本,我更加流利:
import sys
def processOne(fname):
clusters = {}
nextCluster = 1
with open(fname + ".csv", "r") as f:
for line in f:
line = line.strip()
if line == "site,run,id,payload,dir": continue
(site, run, id, payload, dir) = line.split(',')
clind = ",".join((site,run,id))
clust = clusters.setdefault(clind,
{ "i":nextCluster, "1":0, "2":0 })
if clust["i"] == nextCluster:
nextCluster += 1
clust[dir] += 1
if clust[dir] > 20: continue
sys.stdout.write("{label},{i},{dir},{j},{payload}\n"
.format(label=fname,
i=clust["i"],
dir=dir,
j=clust[dir],
payload=payload))
sys.stdout.write("label,stream,dir,i,payload\n")
for fn in sys.argv[1:]: processOne(fn)
并从R脚本调用它:
all <- read.csv(pipe("python preprocess.py A B C", open="r"))
在五秒钟内完成。
所以问题是:在R中执行此操作的正确的方式是什么?不是这个具体的任务,而是这类问题。在分析数据之前,我几乎总是需要对数据进行混洗,而且在其他语言中它几乎总是变得容易 - 无论是编写代码还是计算机执行它。这让我觉得我只使用R作为ggplot2
的接口,如果我学会matplotlib
,我可能会从长远来看节省时间。
答案 0 :(得分:8)
用于完成所需步骤的R代码:
- &#34;其中&#39;标签&#39;源自CSV文件的名称; &#34;
filvec <- list.files(<path>)
for (fil in filvec) { #all the statements will be in the loop body
dat <- read.csv(fil)
dat$label <- fil # recycling will make all the elements the same character value
- &#34; &#39;流&#39;是一个序列号,分配给&#39; site&#39;,&#39; run&#39;和&#39; id&#39;在一个文件中(因此,仅在&#39;标签&#39;中唯一); &#34;
dat$stream <- as.numeric( with(dat, interaction(site, run, id) ) )
- &#34; &#39;我&#39;是每个&#39;流中的行号; &#34;
dat$i <- ave(dat$site, # could be any column since we are not using its values
dat$stream, # 'ave' passes grouped vectors, returns same length vector
FUN= function(x) 1:length(x) )
- &#34;和&#39; dir&#39;和&#39;有效载荷&#39;直接取自原始文件。&#34;
# you can refer to them by name or column number
- &#34;我还想丢弃除了每个流的前20行以外的所有行。 &#34;
out <- dat[dat$i <= 20, # logical test for the "first 20"
c('label','stream','dir','i','payload') ] # chooses columns desired
} # end of loop
实际上,这将覆盖三个&#39;数据&#39;文件。 (因此,对于速度检查的一次性测试,主要是有用的。)你可以做最后一次调用:
assign(paste(fil, "out", sep="_"), dat[dat$i <= 20,
c('label','stream','dir','i','payload') ] )
答案 1 :(得分:6)
data.table
软件包通常会加快对大到大数据框架的操作。
作为一个例子,下面的代码将三个500,000行data.frames作为输入,并在我的非常强大的笔记本电脑上执行你在约2秒内描述的所有转换。
library(data.table)
## Create a list of three 500000 row data.frames
df <- expand.grid(site=1:2, run=1:2, id=1:2)
df <- data.frame(df, payload=1:1000, dir=rep(1, 5e5))
dfList <- list(df, df, df)
dfNames <- c("firstCSV", "secondCSV", "thirdCSV")
## Manipulate the data with data.table, and time the calculations
system.time({
outputList <-
lapply(1:3, FUN = function(ii) {
label <- dfNames[ii]
df <- dfList[[ii]]
dt <- data.table(df, key=c("site", "run", "id"))
groups <- unique(dt[,key(dt), with=FALSE])
groups[, stream := seq_len(nrow(groups))]
dt <- dt[groups]
# Note: The following line only keeps the first 3 (rather than 20) rows
dt <- dt[, head(cbind(.SD, i=seq_len(.N)), 3), by=stream]
dt <- cbind(label,
dt[,c("stream", "dir", "i", "payload"), with=FALSE])
df <- as.data.frame(dt)
return(df)
})
output <- do.call(rbind, outputList)
})
## user system elapsed
## 1.25 0.18 1.44
## Have a look at the output
rbind(head(output,4), tail(output,4))
编辑:在2012年5月8日,通过替换此行,我将上述运行时间减少了约25%:
dt <- dt[, head(cbind(.SD, i=seq_len(.N)), 3), by=stream]
这两个:
dt <- cbind(dt, i = dt[, list(i=seq_len(.N)), by=stream][[2]])
dt <- dt[i<=3,] # Note: This only keeps the 1st 3 (rather than 20) rows