【问题标题】:Subtract values of a single row from all relevant columns in a data frame从数据框中的所有相关列中减去单行的值
【发布时间】:2016-01-27 11:52:12
【问题描述】:

我有以下数据集:

foo=data.frame(index=rep(1:10,3),
               type=rep(c("A","B","C"),each=10),
               ping=rnorm(30),
               pong=runif(30))

我想将index==5type=="B"pingpong 列的值减去pingpong 的整个列。 这有效:

vec=matrix(subset(foo,index==5 & type=="B",select=ping:pong),2,1)
foo[,c("ping","pong")]=foo[,c("ping","pong")]-vec

但是,令我惊讶的是,我必须将 vec 指定为列向量,而不是行向量。我原以为我需要将相同的行向量减去foo 的所有(相似的子集)行。你能解释这是为什么吗?此外,如果可以使用更简单或更简洁的代码获得相同的结果,请告诉我。

【问题讨论】:

    标签: r vector dataframe


    【解决方案1】:

    你想这样做:

    myselect <- with(foo, index ==5 & type == "B")
    mycol <- c('ping','pong')
    
    foo[, mycol] <- foo[, mycol] - as.list(foo[myselect, mycol])
    

    vec 应该是一个列表,因为列表的减法是逐个元素完成的。这就是你想要的,这也是你实际上正在做的:

    首先,您没有将vec 指定为矩阵。如果你在列表中使用matrix() 而不是as.matrix(),你会得到一个列表。由于数据框本质上是一个列表,matrix() 为您提供了一个带有属性“dim”的列表。该属性使它看起来像一个矩阵,但是:

    > str(vec)
    List of 2
     $ : num 0.704
     $ : num 0.164
     - attr(*, "dim")= int [1:2] 2 1
    

    您在这里使用的是函数matrix() 的副作用。它还删除了其他属性,因此它删除了vecdata.frame 信息并使其成为一个列表。如果vec 仍然是一个数据框,它就行不通了。当两个数据框的大小相同时,您只能使用数学运算符。这不是这里的情况。

    > vec=subset(foo,index==5 & type=="B",select=ping:pong)
    > foo[,c("ping","pong")]-vec
    Error in Ops.data.frame(foo[, c("ping", "pong")], vec) : 
      ‘-’ only defined for equally-sized data frames
    

    您也不应该将其设为矩阵。如果这样做,R 将按列回收矩阵和数据框。这意味着它从 foo$ping 的第一个值中减去 vec 的第一个值,从 foo$ping 的第二个值中减去 vec 的第二个值,再从 foo$ping 的第三个值中减去 vec 的第一个值等等。不管你把矩阵放在哪个方向,它总是一样的(错误的!)结果:

    mytest<- matrix(c(-10,10), nrow = 1)
    mytest2 <- t(mytest)
    myfoo <- foo[,c('ping','pong')]
    all.equal(myfoo - mytest, myfoo - mytest2)
    

    【讨论】:

    • 谢谢!因此,我的解决方案有效,因为它没有做我认为它会做的事情。但是,我可以合理地原谅我期望名为 matrix 的函数(其帮助文档以 matrix creates a matrix from the given set of values 开头)实际上会返回一个矩阵;)
    • @DeltaIV 是的,我不得不承认 R 中的一些东西也让我摸不着头脑 :)
    【解决方案2】:

    您还可以执行以下操作。

    Map(`-`, foo[, c("ping", "pong")], 
        subset(foo, index == 5 & type == "B")[, c("ping", "pong")])
    

    此返回列表,但您可以通过as.data.frame() 转换为data.frame

    Map 接受一个函数和一组向量,并按元素应用函数。请注意,- 是减法函数。在此示例中,Map 被赋予了两个 data.frame 对象,其元素是列。因此,这个Map 操作进行逐列减法。

    更多详情见Advanced R page

    【讨论】:

    • 嘿,酷!我不知道Map。我很欣赏这些信息。但是,我选择了@Joris Meys 的答案,它更具可读性,并且还用我的方法解释了“幕后”发生的事情。
    【解决方案3】:

    你可以尝试做这样的事情:

    foo$ping <- foo$ping - foo[foo$index == 5 & foo$type == 'B', 'ping']
    foo$pong <- foo$pong - foo[foo$index == 5 & foo$type == 'B', 'pong']
    

    如果您有多个匹配 index == 5type == 'B' 的行,您可能需要将要减去的部分包装在某个函数中,例如 minmaxmean,它给出一个值。

    要回答您的问题,您尝试从数据框列中的每一行中减去一个 2x1 矩阵,这与逐行减去两对值相同。您可以尝试一下,看看它是否有效,因为两者都是向量:

    x <- c(10, 20)
    y <- c(5, 10)
    x
    [1] 10 20
    y
    [1]  5 10
    x - y
    [1]  5 10
    

    而这不起作用,因为它会在行上重复应用第二个向量(1 和 2):

    df <- data.frame(x = rep(10, 10), y = rep(5, 10))
    df
        x y
    1  10 5
    2  10 5
    3  10 5
    4  10 5
    5  10 5
    6  10 5
    7  10 5
    8  10 5
    9  10 5
    10 10 5
    df - c(10, 5)
       x  y
    1  0 -5
    2  5  0
    3  0 -5
    4  5  0
    5  0 -5
    6  5  0
    7  0 -5
    8  5  0
    9  0 -5
    10 5  0
    

    然而,像这样逐行操作它是可行的,尽管它会循环慢得多:

    df <- sapply(df, function(x) x - c(10, 5))
          x  y
     [1,] 0 -5
     [2,] 5  0
     [3,] 0 -5
     [4,] 5  0
     [5,] 0 -5
     [6,] 5  0
     [7,] 0 -5
     [8,] 5  0
     [9,] 0 -5
    [10,] 5  0
    

    【讨论】:

    • 他使用的不是矩阵而是列表。将您的 c(10,5) 转换为矩阵,然后重试。你仍然得到相同的(错误的)结果。另请参阅我的帖子,以更详细地解释为什么 vec 在他的代码中是一个列表,以及为什么它应该是。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-06
    • 2019-09-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多