for循环输出相同数据的太多行/编译数据帧



我试图编译一些数据在R工作室从一个循环到一个数据框架。目前,我的代码是在120个试验中关联122个参与者的两个变量。

for (i in unique(adult_pref_1$subject)){
a <- cor.test(adult_pref_1$own_pref[adult_pref_1$subject == i], adult_pref_1$profile_rating_new[adult_pref_1$subject == i]) 
print(paste(colnames(adult_pref_1)[adult_pref_1$subject], " est:", a$estimate, "p=value:", a$p.value))
}

当我执行循环时,生成正确的估计值和p值;但是,在开始打印下一个主题的相关性估计和p值之前,它要打印大约1000多行。我不确定为什么会发生这种情况;理想情况下,我想聚合一行包含单个主题ID、估计值和p值的数据(总计122行)。此外,我如何将这些数据编译成一个数据框架。谢谢你的建议。

这里是一些原始数据

structure(list(sub = c("59917f16e339120001fb8c21_fvHlk:5fbd11ca7025930168297956", 
"59917f16e339120001fb8c21_uK9Bt:5fbd11ca7025930168297956", "59917f16e339120001fb8c21_fvHlk:5fbd11ca7025930168297956", 
"59917f16e339120001fb8c21_fvHlk:5fbd11ca7025930168297956", "59917f16e339120001fb8c21_uK9Bt:5fbd11ca7025930168297956", 
"59917f16e339120001fb8c21_uK9Bt:5fbd11ca7025930168297956"), subject = c("59917f16e339120001fb8c21", 
"59917f16e339120001fb8c21", "59917f16e339120001fb8c21", "59917f16e339120001fb8c21", 
"59917f16e339120001fb8c21", "59917f16e339120001fb8c21"), event = c(94L, 
46L, 96L, 80L, 21L, 52L), timestamp = c("24-Nov-2020 14:30:25", 
"24-Nov-2020 14:10:03", "24-Nov-2020 14:30:38", "24-Nov-2020 14:28:38", 
"24-Nov-2020 14:07:02", "24-Nov-2020 14:10:44"), profile = c("mean", 
"odd", "mean", "mean", "odd", "odd"), rating = c(4, 4, 4, 4, 
3, 3), rt_ms = c(2006, 1333, 1275, 1504, 1911, 1410), image = c("beads_1.png", 
"beads_2.png", "notebook_1.png", "notebook_2.png", "notebook_3.png", 
"notebook_4.png"), trial = c(33L, 45L, 35L, 19L, 20L, 51L), onset_s = c(738.738, 
345.591, 752.789, 631.909, 164.527, 386.536), profile_rating = c(2L, 
5L, 9L, 10L, 3L, 5L), block = c(2L, 1L, 2L, 2L, 1L, 1L), sub_num = c(179L, 
154L, 179L, 179L, 154L, 154L), session = c(1L, 2L, 1L, 1L, 2L, 
2L), own_pref = c(4, 4, 4, 4, 3, 4), cat1 = c(1L, 1L, 1L, 1L, 
1L, 1L), cat2 = c(1L, 1L, 1L, 1L, 1L, 1L), item_num = c(16L, 
17L, 85L, 86L, 87L, 88L), own_pref_nan = c(4, 4, 4, 4, 3, 4), 
profile_rating_new = c(2L, 3L, 5L, 6L, 2L, 3L), PE = c(2, 
1, 1, 2, 1, 0), PE_si = c(2, 1, -1, -2, 1, 0), se_PE = c(0, 
0, 0, 0, 0, 1), pro_PE = c(2, 1, 1, 2, 1, 1)), row.names = c(NA, 
6L), class = "data.frame")

尽量避免在R中使用for循环。

结合group_by()mutate(),可以对每个subject进行相关性测试,并将估计值和p值作为新列添加。

library(dplyr)
adult_pref_1 |>
# Perform the next task separately for each subject
group_by(subject) |>
# Run the tests, add results to new columns 'estimate', 'pvalue'
mutate(estimate = cor.test(own_pref, profile_rating_new)$estimate,
pvalue = cor.test(own_pref, profile_rating_new)$p.value) |> 
# Remove irrelevant columns
select(subject, estimate, pvalue) |> 
# Remove duplicate rows
distinct(subject, .keep_all = TRUE)

输出:

#> # A tibble: 1 × 3
#> # Groups:   subject [1]
#>   subject                  estimate pvalue
#>   <chr>                       <dbl>  <dbl>
#> 1 59917f16e339120001fb8c21    0.447  0.374

由reprex包(v2.0.1)创建于2022-06-14

不需要使用显式的for循环来完成此操作。事实上,我相信R编程的一个很好的经验法则是"如果你想使用for循环,可能有更好的方法"……

这是一个使用来自tidyverse的group_by()group_map()和来自broom的tidy()的解决方案。group_by对数据帧进行分组,并将管道的其余部分应用于它创建的组。(注意,它不会对数据帧进行排序。)group_map将其参数定义的函数应用于data.frame的组。它返回一个数据帧列表。tidy是一个泛型,它以合理一致的方式将许多统计函数的输出转换为数据帧。

bind_rows()的功能之一是将数据帧列表转换为单个数据帧。

library(broom)
library(tidyverse)
df %>% 
group_by(subject) %>% 
group_map(
function(.x, .y) {
tidy(cor.test(.x$own_pref, .x$profile_rating_new))
},
.keep=TRUE
) %>% 
bind_rows()
# A tibble: 1 × 8
estimate statistic p.value parameter conf.low conf.high method                               alternative
<dbl>     <dbl>   <dbl>     <int>    <dbl>     <dbl> <chr>                                <chr>      
1    0.447         1   0.374         4   -0.572     0.924 Pearson's product-moment correlation two.sided

虽然Limey和Andrea M的答案要好得多,但如果你执意要继续使用for循环(希望是为了更好地理解),这将是可行的。如前所述,这是低效且非理想的代码。

首先,我们用我们想要的列和长度初始化一个数据帧:


resultsdf <- data.frame("ID" = character(length(unique(adult_pref_1$subject))), 
"estimate" = numeric(length(unique(adult_pref_1$subject))), 
"pvalue" = numeric(length(unique(adult_pref_1$subject))))

然后使用for循环填充。


for (i in 1: length(unique(adult_pref_1$subject))){
this.subject <- unique(adult_pref_1$subject)[i]
a <- cor.test(adult_pref_1$own_pref[adult_pref_1$subject == this.subject], 
adult_pref_1$profile_rating_new[adult_pref_1$subject == this.subject]) 
resultsdf[i,] <- data.frame(this.subject, a$estimate, a$p.value)
print(results.df[i,])
}

你的问题在这里:paste(colnames(adult_pref_1)[adult_pref_1$subject]

我不知道这应该是如何工作的,但你可以看到我是如何做到的。

最新更新