R 如何压缩此代码?
下面的代码可以变得更像“R”吗 给定数据帧inDF:R 如何压缩此代码?,r,R,下面的代码可以变得更像“R”吗 给定数据帧inDF: V1 V2 V3 V4 1 a ha 1;2;3 A 2 c hb 4 B 3 d hc 5;6 C 4 f hd 7 D 我想进去 查找“V3”列具有多个值的所有行 以“;”分隔 然后复制相应行的次数
V1 V2 V3 V4
1 a ha 1;2;3 A
2 c hb 4 B
3 d hc 5;6 C
4 f hd 7 D
我想进去
V1 V2 V3 V4
1 a ha 1 A
1 a ha 2 A
1 a ha 3 A
2 c hb 4 B
3 d hc 5 C
3 d hc 6 C
4 f hd 7 D
因此,如果我想从inDF到达outDF,我将编写以下代码:
#load inDF from csv file
inDF <- read.csv(file='example.csv', header=FALSE, sep=",", fill=TRUE)
#search in inDF, on the V3 column, all the cells with multiple values
rowlist <- grep(";", inDF[,3])
# create empty data.frame and add headers from "headDF"
xDF <- data.frame(matrix(0, nrow=0, ncol=4))
colnames(xDF)=colnames(inDF)
#take every row from the inDF data.frame which has multiple values in col3 and break it in several rows with only one value
for(i in rowlist[])
{
#count the number of individual values in one cell
value_nr <- str_count(inDF[i,3], ";"); value_nr <- value_nr+1
# replicate each row a number of times equal with its value number, and transform it to character
extracted_inDF <- inDF[rep(i, times=value_nr[]),]
extracted_inDF <- data.frame(lapply(extracted_inDF, as.character), stringsAsFactors=FALSE)
# split the values in V3 cell in individual values, place them in a list
value_ls <- str_split(inDF[i, 3], ";")
#initialize f, to use it later to increment both row number and element in the list of values
f = 1
# replace the multiple values with individual values
for(j in extracted_inDF[,3])
{
extracted_inDF[f,3] <- value_ls[[1]][as.integer(f)]
f <- f+1
}
#put all the "demultiplied" rows in xDF
xDF <- merge(extracted_inDF[], xDF[], all=TRUE)
}
# delete the rows with multiple values from the inDF
inDF <- inDF[-rowlist[],]
#create outDF
outDF <- merge(inDF, xDF, all=TRUE)
#从csv文件加载inDF
inDF在这种情况下,您可以使用split-apply-combine范式来重塑数据
您希望按行分割inDF
,因为您希望对每一行分别进行操作。我在这里使用了split
函数将其按行拆分:
spl = split(inDF, 1:nrow(inDF))
spl
是一个列表,其中包含inDF
中每行的一行数据帧
接下来,您需要应用一个函数来将分割的数据转换为所需的最终格式。在这里,我将使用lappy
函数转换1行数据帧,使用strsplit
将变量V3
分解为相应的部分:
transformed = lapply(spl, function(x) {
data.frame(V1=x$V1, V2=x$V2, V3=strsplit(x$V3, ";")[[1]], V4=x$V4)
})
transformed
现在是一个列表,其中第一个元素具有3行数据帧,第三个元素具有2行数据帧,第二个和第四个元素具有1行数据帧
最后一步是使用do.call
和rbind
功能将此列表合并成outDF
。这与使用转换的列表的所有元素调用rbind
具有相同的效果
outDF = do.call(rbind, transformed)
这将产生所需的最终数据帧:
outDF
# V1 V2 V3 V4
# 1.1 a ha 1 A
# 1.2 a ha 2 A
# 1.3 a ha 3 A
# 2 c hb 4 B
# 3.1 d hc 5 C
# 3.2 d hc 6 C
# 4 f hd 7 D
我不确定我是不是在谈论你是用“正确”还是“错误”的方式使用R。。。我通常只是用它来回答有关堆栈溢出的问题。:-)
但是,有许多方法可以改进代码。对于初学者来说,是的,您应该尝试熟悉预定义的函数。它们通常会更加高效,并且会使您的代码对同一语言的其他用户更加透明。尽管您简洁地描述了您想要实现的目标,而且我几乎马上就知道了答案,但我发现您的代码让人望而生畏
我将把您的问题分为两个主要部分:(1)拆分数据和(2)将其与原始数据集重新组合
对于第1部分:strsplit
:您显然知道您需要的一些函数,或者至少知道您需要的主要函数:strsplit
。如果您使用strsplit
,您将看到它返回一个列表
,但您需要一个简单的向量
。你怎么去那里?查找未列出的
。问题的第一部分现在已经解决了
对于第2部分:首先需要确定需要复制原始数据集的每一行的次数。为此,您可以钻取列表(例如,使用l/s/v-apply
)并计算每个项目的长度。我选择了sapply
,因为我知道它会创建一个向量,我可以使用rep
然后,如果你已经玩了足够多的data.frame
s,特别是在提取数据时,你会意识到mydf[c(1,1,1,2),]
将产生一个data.frame
,其中第一行被重复两次。知道了这一点,我们可以使用刚刚进行的length
计算来“扩展”原始data.frame
最后,使用扩展的data.frame
,我们只需要用未列出的值替换相关列
这就是上面的行动。我已将您的数据集命名为“mydf”:
根据正确编码的规则:永远不要重新发明轮子。它浪费时间,你很可能会犯错误,或者至少会想出一个非最优的解决方案。我同意不重新发明轮子。我不确定我是否确切知道如何使用这个轮子。对于上面的例子,您对正确使用R有什么建议吗?我认为在开始时,编写自己的代码是一种很好的做法,特别是因为您不知道有很多常用工具可以帮助解决这个问题。当我开始时,编写循环和一步一步地显式执行我想要的操作要快得多。随着您对语言和软件包越来越熟悉,使用内置函数可以更快地完成工作,代码也会更加简洁和优雅。@rawr很好,tho'one的“最终”解决方案应该与最初的学习尝试有很大不同:-)感谢各位的回答。我既同意不重新发明轮子,也同意一开始可能有必要这样做。我已经重新表述了这个问题,因为我想看看上面的例子中“类似R”的代码是什么样子的。有趣的方法(+1)但这样做似乎是按行进行的,特别是对data.frame的所有调用,从时间上来说,这将是非常昂贵的。当你说从时间上来说是昂贵的,“你是说CPU时间吗?”阿南达马托同意,速度要慢得多(在我的电脑上是4毫秒,而不是0.2毫秒)。我认为split/apply/combine是我在编写R时学习到的最重要的范例,所以我认为展示这种方法很重要。@CLM,我认为josilber在他们最后的评论中谈到了其中的一些。在使用R时,了解对什么类型的对象执行什么类型的操作会比较慢会很有帮助。通过将“transformed”中的匿名函数替换为类似于cbind(x[,c(“V1”,“V2”,“V4”)],V3=strsplit(x[,“V3”],“;”,fixed=TRUE)[[1]],row.names=NULL)
,可以加快这里的代码速度。感谢您的代码示例和解释。它确实简短。让我稍作停顿的是,对于“转换的”列,给出了manuall
V3 <- strsplit(mydf$V3, ";", fixed=TRUE)
sapply(V3, length) ## How many times to repeat each row?
# [1] 3 1 2 1
## ^^ Use that along with `[` to "expand" your data.frame
mydf2 <- mydf[rep(seq_along(V3), sapply(V3, length)), ]
mydf2$V3 <- unlist(V3)
mydf2
# V1 V2 V3 V4
# 1 a ha 1 A
# 1.1 a ha 2 A
# 1.2 a ha 3 A
# 2 c hb 4 B
# 3 d hc 5 C
# 3.1 d hc 6 C
# 4 f hd 7 D
library(data.table)
DT <- data.table(mydf)
DT2 <- DT[, list(new = unlist(strsplit(as.character(V3), ";", fixed = TRUE))), by = V1]
merge(DT, DT2, by = "V1")
library(splitstackshape)
df2 <- concat.split.multiple(mydf, split.cols="V3", seps=";", direction="long")
df2 <- df2[complete.cases(df2), ] ## Optional, perhaps
df2[order(df2$V1), ] ## Optional, perhaps