【问题标题】:How to subset a flat contingency table in R without losing row & column names?如何在不丢失行名和列名的情况下对 R 中的平面列联表进行子集化?
【发布时间】:2012-04-11 07:53:05
【问题描述】:

我正在使用 ftable 创建一个平面列联表。但是,当我对列联表进行子集化时,R 会删除行名和列名。有没有办法对表进行子集化,以使行名和列名保留在子集表中?这是一个例子:

# Create fake data
Group1 = sample(LETTERS[1:3], 20, replace=TRUE)
Group2 = sample(letters[1:3], 20, replace=TRUE)
Year = sample(c("2010","2011","2012"), 20, replace=TRUE)
df1 = data.frame(Group1, Group2, Year)

# Create flat contingency table with column margin
table1 = ftable(addmargins(table(df1$Group1, df1$Group2, df1$Year), margin=3))

# Select rows with sum greater than 2
table2 = table1[table1[ ,4] > 2, ]

> table1
     2010 2011 2012 Sum

A a     0    1    2   3
  b     2    1    0   3
  c     0    0    0   0
B a     0    1    1   2
  b     2    0    0   2
  c     1    0    1   2
C a     0    1    0   1
  b     1    0    2   3
  c     3    0    1   4

> table2
     [,1] [,2] [,3] [,4]
[1,]    0    1    2    3
[2,]    2    1    0    3
[3,]    1    0    2    3
[4,]    3    0    1    4

请注意 R 如何将子集表转换为矩阵,去除列名和两个级别的行名。如何将 ftable 结构保留在子集表中?

【问题讨论】:

    标签: r formatting subset


    【解决方案1】:

    ftable [通过] 创建“平面”列联表 ...将数据重新排列为 [2D] 矩阵。因此,只需在子集之前使用as.matrix将数据转换为矩阵(如果直接使用as.table,数据将返回到它的更高维度结构)。

    # Create flat contingency table with column margin and variable names
    table1 <- ftable(addmargins(table(Group1 = df1$Group1, 
                                     Group2 = df1$Group2, 
                                     Year = df1$Year), margin=3))
    
    # Convert to matrix
    mat1 <- as.matrix(table1)
    mat2 <- mat1[mat1[ ,4] > 2, ]
    mat2
    
    > mat2
                 Year
    Group1_Group2 2010 2011 2012 Sum
              A_b    3    0    0   3
              A_c    0    2    3   5
              B_b    2    0    1   3
    

    如果您真的不喜欢“_”,请使用gsub 替换。

    dimnames(mat2) <- rapply(dimnames(mat2), gsub, pattern = "_", replacement = " ", how = "replace")
    

    编辑

    或者使用dplyrtidyr 包来提高代码的灵活性和可读性:

    library(dplyr)
    library(tidyr)
    
    df1 %>% 
      group_by(Group1, Group2, Year) %>%
      tally() %>%
      spread(Year, n, fill = 0) %>%
      ungroup() %>% 
      mutate(Sum = rowSums(.[-(1:2)])) %>%
      filter(Sum > 2) %>%
      unite(Name, c(Group1, Group2), sep = " ")
    
    Source: local data frame [5 x 5]
    
       Name  2010  2011  2012   Sum
      (chr) (dbl) (dbl) (dbl) (dbl)
    1   A a     2     1     0     3
    2   A b     1     1     1     3
    3   B b     2     0     2     4
    4   B c     1     2     0     3
    5   C a     1     2     0     3
    

    【讨论】:

      【解决方案2】:

      考虑使用频率的 data.frame。它是一种更好的数据结构,尤其是在您要过滤它时。这是一种使用 reshape 包构建的方法。

      # cast the data into a data.frame
      library(reshape)
      df1$Freq <- 1
      df2 <- cast(df1, Group1 + Group2 ~ Year, fun = sum, value = "Freq")
      df2
      #   Group1 Group2 2010 2011 2012
      # 1      A      a    0    0    1
      # 2      A      b    1    1    3
      # 3      A      c    0    0    1
      # 4      B      a    1    2    0
      # 5      B      b    1    1    0
      # 6      B      c    0    0    1
      # 7      C      a    2    0    1
      # 8      C      b    2    0    0
      # 9      C      c    0    0    2
      
      # add a column for the `Sum` of frequencies over the years
      df2 <- within(df2, Sum <- `2010` + `2011` + `2012`)
      df2
      #   Group1 Group2 2010 2011 2012 Sum
      # 1      A      a    0    0    1   1
      # 2      A      b    1    1    3   5
      # 3      A      c    0    0    1   1
      # 4      B      a    1    2    0   3
      # 5      B      b    1    1    0   2
      # 6      B      c    0    0    1   1
      # 7      C      a    2    0    1   3
      # 8      C      b    2    0    0   2
      # 9      C      c    0    0    2   2
      
      df2[df2$Sum > 2, ]
      #   Group1 Group2 2010 2011 2012 Sum
      # 2      A      b    1    1    3   5
      # 4      B      a    1    2    0   3
      # 7      C      a    2    0    1   3
      

      【讨论】:

        【解决方案3】:

        结果将不再是ftable 对象, 因为缺少某些组合。

        但是你可以用一个矩阵来代替,带有行和列的名称。

        ftable_names <- function(x, which="row.vars") {
          # Only tested in dimensions 1 and 2
          rows <- as.vector(Reduce( 
            function(u,v) t(outer(as.vector(u),as.vector(v),paste)), 
            attr(x, which), 
            "" 
          ))
        }
        i <- table1[ ,4] > 2
        table2 <- table1[i,]
        rownames(table2) <- ftable_names(table1, "row.vars")[i]
        colnames(table2) <- ftable_names(table1, "col.vars")
        table2
        
        #      2010  2011  2012  Sum
        # A a     1     2     0    3
        # A c     0     0     3    3
        # B c     0     3     0    3
        # C a     3     1     1    5
        

        【讨论】:

          猜你喜欢
          • 2017-03-19
          • 2021-04-29
          • 2012-10-20
          • 1970-01-01
          • 2018-08-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多