R 如何压缩此代码?

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”列具有多个值的所有行 以“;”分隔 然后复制相应行的次数

下面的代码可以变得更像“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”列具有多个值的所有行 以“;”分隔
  • 然后复制相应行的次数等于“V3”列中单个值的数量
  • 然后每个复制行在“V3”列中只接收一个初始值
  • 很快,output data.frame(=outDF)将如下所示:

        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