将列联表转换为长格式数据框。

6
考虑您获得了一张类似于这样的汇总交叉表:
kdat <- data.frame(positive = c(8, 4), negative = c(3, 6),
                   row.names = c("positive", "negative"))
kdat
#>          positive negative
#> positive        8        3
#> negative        4        6

现在您想计算Cohen's Kappa,这是一种用于确定两个评定者之间协议的统计量。给定这种格式的数据,您可以使用psych::cohen.kappa

psych::cohen.kappa(kdat)$kappa
#> Warning in any(abs(bounds)): coercing argument of type 'double' to logical
#> [1] 0.3287671

这让我很烦恼,因为我更喜欢我的数据是长而细的,这样就可以使用 irr::kappa2。由于一些主观原因,我更喜欢类似的函数。所以我编写了这个函数来重新格式化我的数据:

longify_xtab <- function(x) {
  nm <- names(x)
  # Convert to table
  x_tab <- as.table(as.matrix(x))
  # Just in case there are now rownames, required for conversion
  rownames(x_tab) <- nm
  # Use appropriate method to get a df
  x_df <- as.data.frame(x_tab)

  # Restructure df in a painful and unsightly way
  data.frame(lapply(x_df[seq_len(ncol(x_df) - 1)], function(col) {
    rep(col, x_df$Freq)
  }))
}

该函数返回以下格式:
longify_xtab(kdat)
#>        Var1     Var2
#> 1  positive positive
#> 2  positive positive
#> 3  positive positive
#> 4  positive positive
#> 5  positive positive
#> 6  positive positive
#> 7  positive positive
#> 8  positive positive
#> 9  negative positive
#> 10 negative positive
#> 11 negative positive
#> 12 negative positive
#> 13 positive negative
#> 14 positive negative
#> 15 positive negative
#> 16 negative negative
#> 17 negative negative
#> 18 negative negative
#> 19 negative negative
#> 20 negative negative
#> 21 negative negative

...这使得您可以通过irr::kappa2计算Kappa:

irr::kappa2(longify_xtab(kdat))$value
#> [1] 0.3287671

我的问题是:
有没有更好的方法来处理这个问题(使用基本R或者一个软件包)?这似乎是一个相对简单的问题,但是当我尝试去解决它时,我发现它异常棘手,至少在我的头脑中是这样。

2个回答

6
kdat <- data.frame(positive = c(8, 4), 
                   negative = c(3, 6),
                   row.names = c("positive", "negative"))

library(tidyverse)

kdat %>%
  rownames_to_column() %>%            # set row names as a variable
  gather(rowname2,value,-rowname) %>% # reshape
  rowwise() %>%                       # for every row
  mutate(value = list(1:value)) %>%   # create a series of numbers based on the value
  unnest(value) %>%                   # unnest the counter
  select(-value)                      # remove the counts

# # A tibble: 21 x 2
#    rowname  rowname2
#      <chr>    <chr>   
# 1 positive positive
# 2 positive positive
# 3 positive positive
# 4 positive positive
# 5 positive positive
# 6 positive positive
# 7 positive positive
# 8 positive positive
# 9 negative positive
# 10 negative positive
# # ... with 11 more rows

谢谢,这是一个不错的tidyverse替代方案,但我认为由于问题的一般性质,我会接受基本的R答案。管道很好! - Jemus42

4
以下是我使用的一些公共领域代码,来自:http://www.cookbook-r.com/Manipulating_data/Converting_between_data_frames_and_contingency_tables/,可以完全满足您的要求。请注意,保留了HTML标签。
# Convert from data frame of counts to data frame of cases.
# `countcol` is the name of the column containing the counts
countsToCases <- function(x, countcol = "Freq") {
    # Get the row indices to pull from x
    idx <- rep.int(seq_len(nrow(x)), x[[countcol]])

    # Drop count column
    x[[countcol]] <- NULL

    # Get the rows from x
    x[idx, ]
}

1
所以,对于操作示例:d = as.data.frame(as.table(as.matrix(kdat))) ; countsToCases(d) - user20650
现在我感到有些傻,因为当我开始学习R语言时,我曾经阅读过整本菜谱,并且我知道我曾经浏览过这个函数。感谢您的答案,我将接受这个作为解决方案,因为它使用基本的R语言并且已经发布在公共领域中。 - Jemus42

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接