我正在为生态学学习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
错误。
我尝试了很多方法,但是我只是从网上可以学到的东西中学习。任何帮助将不胜感激。谢谢!
答案 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