代码之家  ›  专栏  ›  技术社区  ›  aL3xa

按列表对data.frame进行子集,并按行对每个部分应用函数

  •  2
  • aL3xa  · 技术社区  · 16 年前

    这似乎是典型的 plyr 问题,但我有不同的想法。 下面是我要优化的函数(跳过 for 循环)。

    # dummy data
    set.seed(1985)
    lst <- list(a=1:10, b=11:15, c=16:20)
    m <- matrix(round(runif(200, 1, 7)), 10)
    m <- as.data.frame(m)
    
    
    dfsub <- function(dt, lst, fun) {
        # check whether dt is `data.frame`
        stopifnot (is.data.frame(dt))
        # check if vectors in lst are "whole" / integer
        # vector elements should be column indexes
        is.wholenumber <- function(x, tol = .Machine$double.eps^0.5)  abs(x - round(x)) < tol
        # fall if any non-integers in list
        idx <- rapply(lst, is.wholenumber)
        stopifnot(idx)
        # check for list length
        stopifnot(ncol(dt) == length(idx))
        # subset the data
        subs <- list()
        for (i in 1:length(lst)) {
                # apply function on each part, by row
                subs[[i]] <- apply(dt[ , lst[[i]]], 1, fun)
        }
        # preserve names
        names(subs) <- names(lst)
        # convert to data.frame
        subs <- as.data.frame(subs)
        # guess what =)
        return(subs)
    }
    

    现在是一个简短的演示…实际上,我正要解释我的初衷。我想把一个 data.frame 通过聚集的载体 list 对象。由于这是心理学研究中伴随着数据操作的函数的一部分代码,您可以考虑 m 作为人格问卷的结果(10名受试者,20名变量)。列表中的向量包含定义问卷子量表(如个性特征)的列索引。每个子刻度都由若干项(中的列)定义 数据帧 )如果我们假设每个分量表的分数都是 sum (或其他函数)行值(每个受试者问卷中该部分的结果),您可以运行:

    > dfsub(m, lst, sum)
        a  b  c
    1  46 20 24
    2  41 24 21
    3  41 13 12
    4  37 14 18
    5  57 18 25
    6  27 18 18
    7  28 17 20
    8  31 18 23
    9  38 14 15
    10 41 14 22
    

    我看了一眼这个函数,我必须承认这个小循环根本没有破坏代码……但是,如果有更简单/有效的方法,请告诉我!

    4 回复  |  直到 13 年前
        1
  •  7
  •   Collin    16 年前

    我将采用不同的方法,将所有内容都保留为数据帧,以便您可以使用merge和ddply。我认为您会发现这种方法更通用一些,并且更容易检查每个步骤是否正确执行。

    # Convert everything to long data frames
    m$id <- 1:nrow(m)
    
    library(reshape)
    obs <- melt(m, id = "id")
    obs$variable <- as.numeric(gsub("V", "", obs$variable))
    
    varinfo <- melt(lst)
    names(varinfo) <- c("variable", "scale")
    
    # Merge and summarise
    obs <- merge(obs, varinfo, by = "variable")
    
    ddply(obs, c("id", "scale"), summarise, 
      mean = mean(value), 
      sum = sum(value))
    
        2
  •  2
  •   Yorgos    16 年前

    装入PLYR包装后,更换

    subs <- list()
        for (i in 1:length(lst)) {
                # apply function on each part, by row
                subs[[i]] <- apply(dt[ , lst[[i]]], 1, fun)
        }
    

    具有

    subs <- llply(lst,function(x) apply(dt[,x],1,fun))
    
        3
  •  0
  •   aL3xa    16 年前

    @哈德利,我已经检查了你的回答,因为它非常简单,便于记账(除此之外,它是更通用的解决方案)。不过,这是我写的不太长的脚本,它只需要 base 软件包(自从我安装 plyr reshape 就在安装r)之后。现在,这是消息来源:

    dfsub <- function(dt, lst, fun) {
            # check whether dt is `data.frame`
            stopifnot (is.data.frame(dt))
            # convert data.frame factors to numeric
            dt <- as.data.frame(lapply(dt, as.numeric))
            # check if vectors in lst are "whole" / integer
            # vector elements should be column indexes
            is.wholenumber <- function(x, tol = .Machine$double.eps^0.5)  abs(x - round(x)) < tol
            # fall if any non-integers in list
            idx <- rapply(lst, is.wholenumber)
            stopifnot(idx)
            # check for list length
            stopifnot(ncol(dt) == length(idx))
            # subset the data
            subs <- list()
            for (i in 1:length(lst)) {
                    # apply function on each part, by row
                    subs[[i]] <- apply(dt[ , lst[[i]]], 1, fun)
            }
            names(subs) <- names(lst)
            # convert to data.frame
            subs <- as.data.frame(subs)
            # guess what =)
            return(subs)
    }
    
        4
  •  0
  •   Alexander Hanysz    13 年前

    对于您的特定示例,单行解决方案是 sapply(lst,function(x) rowSums(m[,x])) (尽管您可以添加更多的行来检查有效输入并输入列名)。

    你还有其他更通用的应用程序吗?或者这可能是 YAGNI ?