当R中满足列条件时,使用for循环填充矩阵

时间:2018-10-19 17:44:02

标签: r for-loop matrix

我正在为生态学学习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错误。

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

3 个答案:

答案 0 :(得分:2)

此建议使用tidyr::spread,尽管使用reshape在基数R中可行。

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的打印输出的一种美化的方式。要验证:

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))

(由于我已经将tidyverse中的一个与tidyr一起带来了,所以我很容易使用dplyr::select(a, -Year) instead of the subset`调用。交给了您以及您使用的任何工具。 )

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

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

答案 1 :(得分:2)

请考虑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

答案 2 :(得分:0)

我不清楚您为什么要以矩阵列表的形式进行此操作,尤其是当原始数据已经为tidy时。如果您只是想将Species从长数据转换为宽数据,则应该这样做。

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