使用(l,s,v)的简单目标群体分析应用并列出

时间:2018-07-18 15:59:28

标签: r list loops apply

我的目标非常简单-从调查中获取数据集并分析分数,每个感兴趣的目标群体给出每个潜在答案的频率。我的代码可以运行,但是它非常笨拙,因此容易出错。我想摆脱这两种情况,但是尽管进行了彻底的研究,但似乎仍然无法做到。

数据看起来像这样(请注意,Var *列包含零,这是不重要的,可以是二进制答案(仅0和1),也可以是多个答案(例如0到4),我需要以备后用):

head(my_data)
ID Gender AgeGroup Var1 Var2 Var3 Var4
 1      1        1    1    1    2    3
 2      1        2    0    0    1    2
 3      2        1    1    1    2    1
 4      1        2    1    1    1    2
 5      2        1    0    1    3    1
 6      1        2    0    1    2    1

理想情况下,我的最终输出应如下所示:

          TG1     TG2     TG3
Var11  60.49%  56.67%  64.17%
Var21  67.3%   56.67%  77.54%
Var31  40.87%  39.44%  42.25%
Var32  27.27%  55.56%  21.23%
Var33  31.86%  5.0%    36.52%

我当前的脚本:
我首先创建数据的子集,其中包含感兴趣的目标组和一个空的数据框,以便以后保存结果:

TG1 <- subset (my_data, my_data$Gender == 1)
TG2 <- subset (my_data, my_data$Gender == 2)
TG3 <- subset (my_data, my_data$Var3 == 1 | my_data$Var3 == 2)

Results <- data.frame (TG1=numeric(0), TG2=numeric(0), TG3=numeric(0))

现在出现了一个巨大的循环:

rownames <- c() #Vector to hold the results temporarily
ColCounter <- 4 #Variable containing the column of the variable currently being calculated

while (ColCounter <= ncol(my_data)) {
  ColCat <- max(my_data[,ColCounter]) #what is the maximum value in the current column?
  Cat <- 1
  while (Cat <= ColCat) {
    t1 <- paste(round(sum(TG1[,ColCounter] == Cat)/nrow(TG1)*100, digits=2), "%", sep="")
    t2 <- paste(round(sum(TG2[,ColCounter] == Cat)/nrow(TG2)*100, digits=2), "%", sep="")
    t3 <- paste(round(sum(TG3[,ColCounter] == Cat)/nrow(TG3)*100, digits=2), "%", sep="")
    Results[nrow(Results)+1,] <- c(t1,t2,t3)
    rownames <- c(rownames, paste (strtrim(names(my_data[ColCounter]), 30), Cat, sep=""))
    Cat <- Cat + 1
    }
  ColCounter <- ColCounter + 1
  }
row.names(Results) <- make.names (rownames, unique=TRUE)

我认为通过编写一个函数来进行计算(可能还有另一个函数来获取每一列的最大类别数)并使用一个apply函数来循环浏览,应该可以更容易地实现这一点。包含目标组的各种数据帧(保存在列表中)。以非常原始的方式编写:

TargetGroups <- lapply(ls(pattern = "TG[1-9]"), get)
names(TargetGroups) <- c("TG1", "TG2", "TG3")

Calc_Perc <- function (...) {
  ...
  }

Results <- lapply(TargetGroups, Calc_Perc)

但是,到目前为止,尽管在列表上和数据框上使用apply都读了这里和其他地方的大量条目,但我的所有方法都失败了。有什么好的方法可以做到这一点吗?

1 个答案:

答案 0 :(得分:0)

这是一种tidyverse方法。您的数据如上所述:

my_data <- read.table(text = "ID Gender AgeGroup Var1 Var2 Var3 Var4
 1      1        1    1    1    2    3
 2      1        2    0    0    1    2
 3      2        1    1    1    2    1
 4      1        2    1    1    1    2
 5      2        1    0    1    3    1
 6      1        2    0    1    2    1", header = TRUE)

首先将上面的组放入列表以方便使用:

groups_list <- list(
  TG1 = subset(my_data, my_data$Gender == 1),
  TG2 = subset(my_data, my_data$Gender == 2),
  TG3 = subset(my_data, my_data$Var3 == 1 | my_data$Var3 == 2)
)

现在使用lapply来应用一个函数,该函数首先将每个子集数据帧转换为长格式,获取每个组中每个答案的比例,然后针对groups_list中的每个数据帧输出这些比例(更多详细信息)在评论中):

list_proportion_dfs <- lapply(names(groups_list), function(x) {
  ## Convert to long format
  long = groups_list[[x]] %>%
    gather(key = var, value = val, -c(ID, Gender, AgeGroup))

  proportions = long %>%
    ### Group by variable and value
    group_by(var, val) %>%
    ### Assign the length of each grouping
    ### to the new variable 'n'
    summarize(n = n()) %>%
    ### Convert to a proportion by dividing
    ### n by the sum of n for the current 
    ### 'var' grouping
    mutate(
      var.combo = paste(var, val, sep = ""),
      x = n / sum(n) * 100
    ) %>% 
    ungroup() %>%
    ### Keep only the important rows
    select(var.combo, x)
  names(proportions) <- c("var.combo", x)
  return(proportions)
})

输出如下:

> list_proportion_dfs
[[1]]
# A tibble: 9 x 2
  var.combo   TG1
  <chr>     <dbl>
1 Var10        50
2 Var11        50
3 Var20        25
4 Var21        75
5 Var31        50
6 Var32        50
7 Var41        25
8 Var42        50
9 Var43        25

[[2]]
# A tibble: 6 x 2
  var.combo   TG2
  <chr>     <dbl>
1 Var10        50
2 Var11        50
...
...

现在,您可以使用Reducemerge(根据this answer)接近所需的结果:

output <- Reduce(function(x, y) merge(x, y, all = TRUE), list_proportion_dfs)

将NA值转换为零:

output[is.na(output)] <- 0

您的(未格式化的)结果如下:

> output
   var.combo TG1 TG2 TG3
1      Var10  50  50  40
2      Var11  50  50  60
3      Var20  25   0  20
4      Var21  75 100  80
5      Var31  50   0  40
6      Var32  50  50  60
7      Var33   0  50   0
8      Var41  25 100  40
9      Var42  50   0  40
10     Var43  25   0  20

我很确定数学是正确的,因为给定的组/变量组合的所有比例都加到100%(示例输出不是这种情况)。如果我误解了您实际要查找的百分比,则可能需要弄乱分组顺序/级别。

为使输出更接近您显示的内容(同时避免使用多个“%”符号,您可以这样做:

rownames(output) <- output$var.combo

output <- select(output, -var.combo)

names(output) <- c(paste(names(output), "(%)"))

> output
      TG1 (%) TG2 (%) TG3 (%)
Var10      50      50      40
Var11      50      50      60
Var20      25       0      20
Var21      75     100      80
Var31      50       0      40
Var32      50      50      60
Var33       0      50       0
Var41      25     100      40
Var42      50       0      40
Var43      25       0      20

但是我想您可能还是用RMarkdown或Excel进行了格式化。