R中的分组xtabs

Group wise xtabs in R

我想为不同的站准备xtabs。但它给了我整体 table 跨站。我使用了以下代码

library(tidyverse)

df %>% group_by(Station) %>% 
  xtabs( ~ Observed + Forecasted, data = .) 

这是给了我

#>        Forecasted
#>Observed   1   3   4   5   9
#>       1 132   5  31  31   3
#>       3  16   0   6  13   7
#>       4   6   0  13  23   8
#>       5   4   0  16  33  15
#>       9   0   0   0   2   0

但我希望像

这样的站位输出
Aizawl
#>        Forecasted
#>Observed   1   3   4   5   9
#>       1 132   5  31  31   3
#>       3  16   0   6  13   7
#>       4   6   0  13  23   8
#>       5   4   0  16  33  15
#>       9   0   0   0   2   0

Serchhip
#>        Forecasted
#>Observed   1   3   4   5   9
#>       1 132   5  31  31   3
#>       3  16   0   6  13   7
#>       4   6   0  13  23   8
#>       5   4   0  16  33  15
#>       9   0   0   0   2   0

然后我想将输出导出到 .csv 文件中。

数据

df = structure(list(Station = c("Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", "Aizawl", 
"Aizawl", "Aizawl", "Aizawl", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip", 
"Serchhip", "Serchhip", "Serchhip", "Serchhip", "Serchhip"), 
    Observed = c(1, 1, 1, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 
    1, 1, 1, 1, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 1, 1, 
    1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 4, 1, 1, 4, 1, 3, 1, 1, 
    1, 1, 1, 1, 1, 1, 1, 4, 4, 4, 3, 4, 1, 1, 1, 1, 1, 3, 5, 
    5, 3, 5, 3, 1, 1, 3, 1, 1, 1, 1, 1, 5, 3, 4, 1, 1, 1, 1, 
    1, 3, 1, 4, 1, 1, 1, 1, 1, 4, 4, 5, 1, 5, 4, 5, 5, 5, 5, 
    1, 5, 1, 4, 5, 4, 4, 5, 4, 5, 5, 3, 1, 5, 3, 4, 3, 4, 5, 
    5, 5, 5, 4, 4, 4, 5, 5, 5, 5, 5, 5, 4, 5, 3, 4, 4, 5, 3, 
    5, 4, 4, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 5, 1, 1, 1, 1, 
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
    1, 3, 5, 5, 1, 1, 3, 4, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 
    1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 
    3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 4, 4, 1, 3, 4, 1, 1, 1, 1, 
    1, 1, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
    1, 1, 1, 4, 3, 9, 5, 5, 4, 1, 5, 1, 1, 1, 1, 4, 5, 5, 5, 
    5, 5, 5, 1, 1, 4, 1, 4, 4, 4, 5, 1, 1, 4, 3, 5, 1, 1, 4, 
    3, 5, 3, 4, 5, 3, 4, 4, 5, 5, 3, 4, 5, 5, 5, 5, 5, 4, 4, 
    4, 4, 5, 1, 9, 5, 5), Forecasted = c(1, 1, 1, 5, 5, 1, 1, 
    1, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 
    5, 9, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 1, 1, 1, 1, 
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
    1, 1, 1, 4, 1, 4, 4, 1, 1, 5, 3, 1, 1, 1, 4, 5, 5, 5, 5, 
    1, 1, 1, 5, 5, 1, 5, 5, 5, 9, 4, 5, 4, 4, 4, 3, 4, 4, 1, 
    1, 5, 5, 4, 4, 4, 1, 1, 1, 4, 4, 4, 4, 4, 4, 1, 1, 5, 4, 
    4, 5, 4, 4, 4, 4, 5, 4, 5, 5, 5, 5, 5, 4, 5, 5, 4, 1, 1, 
    4, 4, 5, 5, 5, 5, 1, 4, 5, 5, 1, 4, 4, 9, 9, 9, 9, 9, 9, 
    9, 9, 9, 9, 9, 9, 9, 9, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
    5, 5, 5, 9, 1, 1, 1, 5, 4, 1, 1, 1, 5, 4, 1, 1, 1, 1, 1, 
    1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 9, 5, 5, 1, 1, 1, 1, 1, 
    1, 1, 1, 1, 5, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 1, 1, 
    1, 1, 1, 1, 1, 1, 1, 5, 5, 4, 1, 1, 1, 1, 1, 4, 1, 1, 1, 
    4, 4, 4, 4, 1, 4, 1, 3, 1, 1, 1, 4, 4, 4, 4, 4, 4, 1, 1, 
    1, 4, 4, 3, 5, 5, 5, 4, 3, 5, 5, 5, 5, 5, 4, 5, 5, 5, 4, 
    5, 4, 4, 5, 5, 4, 4, 5, 4, 1, 4, 4, 5, 5, 4, 5, 4, 5, 4, 
    5, 5, 5, 1, 4, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 
    5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 9)), row.names = c(NA, 
364L), class = "data.frame")

如果您想将其写入 CSV,最好将数据保存为 CSV 友好格式。这个输出

Aizawl
#>        Forecasted
#>Observed   1   3   4   5   9
#>       1 132   5  31  31   3
#>       3  16   0   6  13   7
#>       4   6   0  13  23   8
#>       5   4   0  16  33  15
#>       9   0   0   0   2   0

Serchhip
#>        Forecasted
#>Observed   1   3   4   5   9
#>       1 132   5  31  31   3
#>       3  16   0   6  13   7
#>       4   6   0  13  23   8
#>       5   4   0  16  33  15
#>       9   0   0   0   2   0

可能会导致问题,使用 dplyrtidyr 可以轻松准备 CSV 格式友好的输出。

library(dplyr)
library(tidyr)

df %>% 
  group_by(Station, Observed, Forecasted) %>% 
  tally() %>% 
  pivot_wider(names_from = Forecasted, values_from = n) %>% 
  replace(is.na(.), 0) 
# A tibble: 9 x 7
# Groups:   Station, Observed [9]
  Station  Observed   `1`   `3`   `4`   `5`   `9`
  <chr>       <dbl> <int> <int> <int> <int> <int>
1 Aizawl          1    56     2    13    13     1
2 Aizawl          3    12     0     4     8     3
3 Aizawl          4     4     0     9    11     4
4 Aizawl          5     3     0     7    23     9
5 Serchhip        1    76     3    18    18     2
6 Serchhip        3     4     0     2     5     4
7 Serchhip        4     2     0     4    12     4
8 Serchhip        5     1     0     9    10     6
9 Serchhip        9     0     0     0     2     0

此时您可以轻松导出到 CSV,或者直接通过链接管道导出。

df %>% 
  group_by(Station, Observed, Forecasted) %>% 
  tally() %>% 
  pivot_wider(names_from = Forecasted, values_from = n) %>% 
  replace(is.na(.), 0) %>%
  readr::write_csv("my_output.csv")

使用 by.

res <- by(df, df$Station, xtabs, f=~Observed + Forecasted)
res
# df$Station: Aizawl
#          Forecasted
# Observed  1  3  4  5  9
#           1 56  2 13 13  1
#           3 12  0  4  8  3
#           4  4  0  9 11  4
#           5  3  0  7 23  9
# --------------------------------------------------------- 
#   df$Station: Serchhip
#         Forecasted
# Observed  1  3  4  5  9
#           1 76  3 18 18  2
#           3  4  0  2  5  4
#           4  2  0  4 12  4
#           5  1  0  9 10  6
#           9  0  0  0  2  0

## Output .csv
lapply(names(res), function(x) write.csv(res[[x]], paste0("myfile_", x,".csv")))

如果你想对xtabs(3维数组)进行操作而不是listdata.frame,那么把分层变量,即Station,到 xtabs().

中公式的最后位置
res <- xtabs( ~ Observed + Forecasted + Station, df)
res

# , , Station = Aizawl
# 
#         Forecasted
# Observed  1  3  4  5  9
#        1 56  2 13 13  1
#        3 12  0  4  8  3
#        4  4  0  9 11  4
#        5  3  0  7 23  9
#        9  0  0  0  0  0
# 
# , , Station = Serchhip
# 
#         Forecasted
# Observed  1  3  4  5  9
#        1 76  3 18 18  2
#        3  4  0  2  5  4
#        4  2  0  4 12  4
#        5  1  0  9 10  6
#        9  0  0  0  2  0

class(res)
# [1] "xtabs" "table"

要将数组打印到 2 个单独的 csv 文件:

lapply(dimnames(res)$Station, function(x) write.csv(res[,,x], paste0("table_", x, ".csv")))

要将数组转换为 data.frame 格式:

library(tidyr)

res %>%
  as.data.frame() %>%
  pivot_wider(names_from = Forecasted, values_from = Freq)

# # A tibble: 10 x 7
#    Observed Station    `1`   `3`   `4`   `5`   `9`
#    <fct>    <fct>    <int> <int> <int> <int> <int>
#  1 1        Aizawl      56     2    13    13     1
#  2 3        Aizawl      12     0     4     8     3
#  3 4        Aizawl       4     0     9    11     4
#  4 5        Aizawl       3     0     7    23     9
#  5 9        Aizawl       0     0     0     0     0
#  6 1        Serchhip    76     3    18    18     2
#  7 3        Serchhip     4     0     2     5     4
#  8 4        Serchhip     2     0     4    12     4
#  9 5        Serchhip     1     0     9    10     6
# 10 9        Serchhip     0     0     0     2     0