【问题标题】:Using a for loop to fill out a matrix when column conditions are met in R在R中满足列条件时使用for循环填充矩阵
【发布时间】:2018-10-19 17:44:01
【问题描述】:

我学习 R 是为了进行生态研究,我正在尝试编写一个函数来创建多个矩阵。

我的数据框看起来像:

df <- data.frame(Species = c("a", "b", "c", "a", "d", "a", "b", "c", "c", "a", "c", "b", "e"),
             Count = c(2, 3, 1, 3, 4, 1, 2, 1, 1, 3, 2, 4, 1),
             Haul = c(1, 1, 2, 2, 1, 3, 2, 3, 4, 1, 1, 2, 1),
             Year = c(2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2001, 2001, 2001, 2001))

印刷:

Species Count Haul Year
1        a     2    1 2000
2        b     3    1 2000
3        c     1    2 2000
4        a     3    2 2000
5        d     4    1 2000
6        a     1    3 2000
7        b     2    2 2000
8        c     1    3 2000
9        c     1    4 2000
10       a     3    1 2001
11       c     2    1 2001
12       b     4    2 2001
13       e     1    1 2001

我正在寻找一个 for 循环,它将在列表中生成和存储矩阵。这些矩阵将基于每年的运输和物种。

例如,我一直在尝试类似的东西。

for (i in sort(unique(df$Year))) {
ncol <- sort(unique(unlist(df$Species)))
nrow <- sort(unique(unlist(subset(df, Year == i, select=c("Haul")))))
mat <- matrix(0, length(nrow), length(ncol),
              dimnames = list(nrow, ncol))
mat[as.matrix(df[c("Haul", "Species")])] <- df$Count

这一直不起作用。

我正在寻找类似的解决方案

list[[1]]
[["2000"]] a  b  c  d  e
         1 2  3  0  4  0
         2 3  2  1  0  0
         3 1  0  1  0  0
         4 0  0  1  0  0

[["2001"]] a  b  c  d  e 
         1 3  0  2  0  1  
         2 0  4  0  0  0

我们的目标是让列是曾经见过的物种总数,而行是当年的特定收获。然后 for 循环会将矩阵堆叠在一个列表中。

我尝试的主要方法是创建一个归零矩阵并尝试使用mat[as.matrix()] 函数填充数据,但我不断收到subscript out of bound 错误。

我尝试了很多方法,但我只是从网上可以找到的东西中学习。任何帮助将不胜感激。谢谢!

【问题讨论】:

    标签: r for-loop matrix


    【解决方案1】:

    考虑by(按因子拆分数据帧以在子集上运行进程的函数)和table(通过因子组合构建计数列联表的函数)。最终结果是一个命名的矩阵列表。

    matrix_list <- by(df, df$Year, function(sub) {    
        mat <- table(sub$Haul, sub$Species)
        mat[as.matrix(sub[c("Haul", "Species")])] <- sub$Count
    
        return(mat)      
    })
    
    matrix_list$`2000`
    
    #   a b c d e
    # 1 2 3 0 4 0
    # 2 3 2 1 0 0
    # 3 1 0 1 0 0
    # 4 0 0 1 0 0
    
    matrix_list$`2001`
    
    #   a b c d e
    # 1 3 0 2 0 1
    # 2 0 4 0 0 0
    

    【讨论】:

    • 我曾尝试使用xtabstable,但没想到矩阵索引重新分配,不错。
    【解决方案2】:

    此建议使用tidyr::spread,尽管在base R 中使用reshape 是可行的。

    out <- by(df, df$Year, function(a) tidyr::spread(a, Species, Count, fill=0))
    out
    # df$Year: 2000
    #   Haul Year a b c d
    # 1    1 2000 2 3 0 4
    # 2    2 2000 3 2 1 0
    # 3    3 2000 1 0 1 0
    # 4    4 2000 0 0 1 0
    # -------------------------------------------------------------------------------------------- 
    # df$Year: 2001
    #   Haul Year a b c e
    # 1    1 2001 3 0 2 1
    # 2    2 2001 0 4 0 0
    

    从技术上讲,输出是

    class(out)
    # [1] "by"
    

    但这只是提供by-like 打印输出的一种美化方式。验证:

    str(out)
    # List of 2
    #  $ 2000:'data.frame': 4 obs. of  6 variables:
    #   ..$ Haul: num [1:4] 1 2 3 4
    #   ..$ Year: num [1:4] 2000 2000 2000 2000
    #   ..$ a   : num [1:4] 2 3 1 0
    #   ..$ b   : num [1:4] 3 2 0 0
    #   ..$ c   : num [1:4] 0 1 1 1
    #   ..$ d   : num [1:4] 4 0 0 0
    #  $ 2001:'data.frame': 2 obs. of  6 variables:
    #   ..$ Haul: num [1:2] 1 2
    #   ..$ Year: num [1:2] 2001 2001
    #   ..$ a   : num [1:2] 3 0
    #   ..$ b   : num [1:2] 0 4
    #   ..$ c   : num [1:2] 2 0
    #   ..$ e   : num [1:2] 1 0
    #  - attr(*, "dim")= int 2
    #  - attr(*, "dimnames")=List of 1
    #   ..$ df$Year: chr [1:2] "2000" "2001"
    #  - attr(*, "call")= language by.data.frame(data = df, INDICES = df$Year, FUN = function(a) tidyr::spread(a, Species, Count, fill = 0))
    #  - attr(*, "class")= chr "by"
    

    所以我们可以重写类

    class(out) <- "list"
    out
    # $`2000`
    #   Haul Year a b c d
    # 1    1 2000 2 3 0 4
    # 2    2 2000 3 2 1 0
    # 3    3 2000 1 0 1 0
    # 4    4 2000 0 0 1 0
    # $`2001`
    #   Haul Year a b c e
    # 1    1 2001 3 0 2 1
    # 2    2 2001 0 4 0 0
    # attr(,"call")
    # by.data.frame(data = df, INDICES = df$Year, FUN = function(a) tidyr::spread(a, 
    #     Species, Count, fill = 0))
    

    为了简单和演示,我将Year 保留在其中(以防您可能出于某种原因想要保留它),但它也很容易删除:

    out <- by(df, df$Year, function(a) tidyr::spread(subset(a, select=-Year), Species, Count, fill=0))
    

    (由于我已经引入了tidyversetidyr 之一,我可以轻松使用dplyr::select(a, -Year) instead of thesubset` 调用。交给你和你使用的任何工具。)

    我现在承认这是产生data.frames,而不是矩阵。将每个结果的结果转换为适当的矩阵需要更多代码。

    df2m <- function(x) {
      # assume first column should be row names
      rn <- x[[1]]
      out <- as.matrix(x[-1])
      rownames(out) <- rn
      out
    }
    lapply(out, df2m)
    # $`2000`
    #   a b c d
    # 1 2 3 0 4
    # 2 3 2 1 0
    # 3 1 0 1 0
    # 4 0 0 1 0
    # $`2001`
    #   a b c e
    # 1 3 0 2 1
    # 2 0 4 0 0
    

    【讨论】:

    • by 的不错,但可以使用table 完全留在base
    【解决方案3】:

    我不清楚您为什么要将此作为矩阵列表执行,尤其是当您的原始数据已经是 tidy 时。如果您只是想按物种从长数据转换为宽数据,那么应该这样做。

    library(tidyverse)
    
    df %>% 
      #spread Species from long to wide data
      spread(key = Species, value = Count, fill = 0) %>% 
      #Make Year the first column
      select(Year, everything()) %>% 
      #sort by Year and Haul
      arrange(Year, Haul)
    
    Year Haul a b c d e
    2000    1 2 3 0 4 0
    2000    2 3 2 1 0 0
    2000    3 1 0 1 0 0
    2000    4 0 0 1 0 0
    2001    1 3 0 2 0 1
    2001    2 0 4 0 0 0
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-09-29
      • 1970-01-01
      • 2011-09-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多