【问题标题】:Choose one cell per row in data frame在数据框中每行选择一个单元格
【发布时间】:2012-06-09 07:10:58
【问题描述】:

我有一个向量告诉我,对于日期框架中的每一行,应该更新该行中的值的列索引。

> set.seed(12008); n <- 10000; d <- data.frame(c1=1:n, c2=2*(1:n), c3=3*(1:n))
> i <- sample.int(3, n, replace=TRUE)
> head(d); head(i)
  c1 c2 c3
1  1  2  3
2  2  4  6
3  3  6  9
4  4  8 12
5  5 10 15
6  6 12 18
[1] 3 2 2 3 2 1

这意味着对于第 1 行和第 4 行,应更新 c3;对于第 2、3 和 5 行,应更新 c2(除其他外)。在 R 中使用矢量化操作(即没有apply 和朋友)实现这一目标的最简洁方法是什么? 编辑:而且,如果可能的话,没有 R 循环?

我曾考虑将d 转换为矩阵,然后使用一维向量寻址矩阵元素。但是我还没有找到一种干净的方法来从行和列索引计算一维地址。

【问题讨论】:

    标签: r matrix dataframe


    【解决方案1】:

    这是一种方法:

    d[which(i == 1), "c1"] <- "one"
    d[which(i == 2), "c2"] <- "two"
    d[which(i == 3), "c3"] <- "three"
    
       c1  c2    c3
    1   1   2 three
    2   2 two     6
    3   3 two     9
    4   4   8 three
    5   5 two    15
    6 one  12    18
    

    【讨论】:

    • 谢谢。这需要对列进行循环,这还不错。不过,是否有完全矢量化的解决方案?
    【解决方案2】:

    使用您的示例数据,并且仅使用前几行(D 和下面的I),您可以通过矩阵轻松地做您想做的事情。

    set.seed(12008)
    n <- 10000
    d <- data.frame(c1=1:n, c2=2*(1:n), c3=3*(1:n))
    i <- sample.int(3, n, replace=TRUE)
    ## just work with small subset
    D <- head(d)
    I <- head(i)
    

    首先,将D转换成矩阵:

    dmat <- data.matrix(D)
    

    接下来计算与I 指示的行和列对应的矩阵的向量表示的索引。为此,使用seq_along(I) 很容易生成行索引和列索引(由I 给出),在这个简单的示例中是向量1:6。要计算我们可以使用的向量索引:

    (I - 1) * nrow(D) + seq_along(I)
    

    其中第一部分((I - 1) * nrow(D))为我们提供了行数的正确倍数(此处为 6)来索引Ith 列的开头。然后我们添加行索引以获得Ith 列中第 n 个元素的索引。

    使用它,我们只需使用"["dmat 进行索引,将其视为向量。 "[" ("[&lt;-") 的替换版本允许我们在一行中进行替换。在这里,我将指示的元素替换为 NA,以便更容易看到识别出正确的元素:

    > dmat
      c1 c2 c3
    1  1  2  3
    2  2  4  6
    3  3  6  9
    4  4  8 12
    5  5 10 15
    6  6 12 18
    > dmat[(I - 1) * nrow(D) + seq_along(I)] <- NA
    > dmat
      c1 c2 c3
    1  1  2 NA
    2  2 NA  6
    3  3 NA  9
    4  4  8 NA
    5  5 NA 15
    6 NA 12 18
    

    【讨论】:

    • 谢谢。但是这个构造(I - 1) * nrow(D) + seq_along(I) 是否封装在一些可公开访问的函数中? (更一般地说,我正在寻找类似matrix.index(m, r, c) 的东西,其中r 是行向量,c 是列向量。我知道如何构建它,但这必须在 R 核心的某个地方,不是吗?)矩阵寻址在内部是如何工作的?
    • 不,不是。 I 是列(c 在您的符号中),seq_along(I) 是行(或 r)。由于您的示例,我使用了我所做的事情,尽管i 是一个向量,只要根据您的示例的行数,所以我的代码即使对于大的i 仍然有效。最后一点,研究 C 代码或 R 内部文档;这一切都是在 C 中完成的,但请注意,就 R 而言,矩阵只是一个向量,其元素按列堆叠,即首先填充列,因此当将矩阵视为向量时,col 1 的所有行首先出现,然后是第 2 列的行等。
    • @user946850 也就是说,没有什么可以阻止您使用上面显示的示例编写matrixIndex()。您可以将其放入您自己的私有包中,并在每个 R 会话开始时加载(或安排自动加载)。
    • @user946850 为什么?这是一个简单的操作,很容易做饭。 R 核心开发团队的立场是,他们希望核心合理精简,以最大限度地减少维护负担,或包含他们想要/需要进行研究的东西等。包中的代码/功能是一流的 R公民,因此无需将所有内容都包含在基础 R 中。请注意,更复杂的索引作业(选择矩阵的上三角形或下三角形)包含在基础 R 中,但不包括我知道,你提到的简单案例。它很可能在 CRAN 上的另一个包中?
    • @user946850 & Gavin (+1) -- 看起来 R-core 的人确实在其中看到了一些实用程序,并将它(嗯,它的等效功能)添加到当前的 R-devel。 (我刚刚在下面添加的答案中有更多详细信息)。
    【解决方案3】:

    如果您愿意先将 data.frame 转换为矩阵,则可以使用两列矩阵索引要替换的元素。 (从R-2.16.0 开始,可以直接使用 data.frames。)索引矩阵的第一列应该有行索引,第二列应该有列索引。

    这是一个例子:

    ## Create a subset of the your data
    set.seed(12008); n  <- 6 
    D  <- data.frame(c1=1:n, c2=2*(1:n), c3=3*(1:n))
    i <- seq_len(nrow(D))            # vector of row indices
    j <- sample(3, n, replace=TRUE)  # vector of column indices 
    ij <- cbind(i, j)                # a 2-column matrix to index a 2-D array 
                                     # (This extends smoothly to higher-D arrays.)  
    
    ## Convert it to a matrix    
    Dmat <- as.matrix(D)
    
    ## Replace the elements indexed by 'ij'
    Dmat[ij] <- NA
    Dmat
    #      c1 c2 c3
    # [1,]  1  2 NA
    # [2,]  2 NA  6
    # [3,]  3 NA  9
    # [4,]  4  8 NA
    # [5,]  5 NA 15
    # [6,] NA 12 18
    

    R-2.16.0 开始,您将能够对数据帧使用相同的语法(即无需先将数据帧转换为矩阵)。

    来自R-develNEWS 文件:

    现在支持通过两列数字索引对数据帧进行矩阵索引以进行替换和提取。

    使用当前的R-devel 快照,如下所示:

    D[ij] <- NA
    D
    #   c1 c2 c3
    # 1  1  2 NA
    # 2  2 NA  6
    # 3  3 NA  9
    # 4  4  8 NA
    # 5  5 NA 15
    # 6 NA 12 18
    

    【讨论】:

    • 是否已移植到 2.15.1 分支? R-devel 通常意味着下一个次要版本,即 2.16.x。
    • @GavinSimpson -- 不错的收获。谢谢。再看一遍,我现在看到突出的注释“R [...] 的 r59537 开发快照最终将变为 R-2.16.0”。将相应地编辑我的帖子。
    • 对矩阵/数组也有类似的支持吗?
    • @user946850 -- 已经有了!试试这个,看看它是如何与数组一起工作的:a &lt;- array(1:64, dim=c(4,4,4)); a[cbind(1:4, c(4,3,1,2), 1:4)] &lt;- NA.
    • @JoshO'Brien:您是否介意将其单独添加为答案,或者编辑此答案?这确实比 Gavin 提议的要干净得多。 (文档中是否提到了这种寻址方式?)
    猜你喜欢
    • 2019-03-01
    • 2015-08-16
    • 2018-06-24
    • 1970-01-01
    • 2021-03-30
    • 2023-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多