Примените функцию к каждой группе

#r

#r

Вопрос:

У меня есть этот набор данных:

 Alt;- c(10,20,10,31,51,1,60,1,02,0,12,0,20,1,0,0,0,0,1,0,1,1,1) Blt;- c(1,0,0,1,1,1,0,1,1,0,1,1,0,0,0,1,0,0,0,0,0,0,0) Clt;- c(1,0,0,1,1,1,0,1,1,0,1,1,0,0,0,1,0,0,0,0,0,0,1) SUB lt;- c(1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2) dat lt;- as.data.frame(cbind(SUB,B,A,C))  

Я написал функцию, вычисляющую cor среди A/B, B/C, C/A.

 Z lt;- function(a,b,c) {  cor1 = cor(a,b)  cor2 = cor(b,c)  cor3 = cor(c,a)    x = c(cor1,cor2,cor3)    return(x) }  

если я наберу

 Z(dat$A, dat$B,dat$C)  

Я получаю вектор результатов:

 gt; [1] 0.11294312 0.91417410 0.06457059  

Мне нужно привязать свою функцию к переменной SUB и получить матрицу, строки которой являются одинаковыми среди A/B, B/C, C/A для каждой подстановки.

Например:

 A/B B/C C/A SUB1 0.11294312 0.91417410 0.06457059 SUB2 0.10335312 0.96744677 0.16356059  

Спасибо, с наилучшими пожеланиями

Комментарии:

1. К вашему сведению, обычно плохая практика называть переменные или функции после базовых функций/примитивов R; try обычно используется другими пакетами/функциями в форме защитного программирования. В то время как R, как правило, подходит для определения того, что использовать в каждом контексте, устранение неполадок, основанных на этом, будет значительно разочаровывать.

2. Пожалуйста, будьте осторожны с образцами данных; C это длина 22, все остальные-длина 23. R перерабатывает a 1 в 23-ю позицию, не уверен, что это проблема для вас (хотя это почти наверняка изменит корреляции).

3. Спасибо, я все исправил.

4. @r2evans Я полностью не согласен с утверждением, что это плохая практика. Наоборот: столкновения имен в реальном коде неизбежны, и перепрыгивание через обручи, чтобы заинтересовать их, приводит к запутанным, многословным именам, которые в конечном итоге затрудняют чтение кода. Тем не менее, это, try в частности, редко подходящее название. Программисты должны вместо этого использовать пространства имен, что является проверенным и верным способом решения этой проблемы. R к сожалению, это не поощряется, но пакет «коробка» облегчает определение имен.

5. Я не согласен с советом использовать пространства имен, но … сколько раз вы помогали кому-то, кто непреднамеренно использовал неправильную функцию/объект? R не всегда наиболее откровенен с полезными сообщениями об ошибках в этом отношении, по крайней мере, для неопытного пользователя R. Как более опытный разработчик, мне удобно называть свои переменные произвольно (в том числе так же, как примитивы/функции), но новые пользователи R, как правило, не распознают знаки. Как бы то ни было, это субъективно. (И да, «соглашения об именах»- одна из двух самых сложных вещей в CS: -)

Ответ №1:

основание R

Вы можете разделиться, by а затем рекомбинировать.

 do.call(rbind, by(dat, dat$SUB, function(x) Z(x$A, x$B, x$C))) # [,1] [,2] [,3] # 1 -0.1534126 1.0000000 -0.15341258 # 2 0.1081781 0.8215838 0.04608456  

Имена строк 1 и 2 сами SUB значения; если SUB это более «интересно», чем подсчет чисел, это будет более очевидно. Имена столбцов можно применять тривиально.

dplyr

 library(dplyr) dat %gt;%  group_by(SUB) %gt;%  summarize(as.data.frame(matrix(Z(A, B, C), nr = 1))) # # A tibble: 2 x 4 # SUB V1 V2 V3 # lt;dblgt; lt;dblgt; lt;dblgt; lt;dblgt; # 1 1 -0.153 1.00 -0.153  # 2 2 0.108 0.822 0.0461  

Ответ №2:

Попробуйте split в сочетании с sapply

 sapply( split(dat,dat$SUB), function(x) Z(x["A"],x["B"],x["C"]) )  1 2 [1,] -0.1534126 0.10817808 [2,] 1.0000000 0.82158384 [3,] -0.1534126 0.04608456  

Ответ №3:

На самом деле в вашей функции нет необходимости, если вы используете матрицу upper.tri cor отношений. В последнее время вы можете сделать это очень легко, проложив трубопровод:

 sapply(unique(dat$SUB), (i) cor(dat[dat$SUB == i, -1]) |gt; {(x) x[upper.tri(x)]}()) # [,1] [,2] # [1,] -0.1534126 0.10817808 # [2,] 1.0000000 0.82158384 # [3,] -0.1534126 0.04608456  R.version.string # [1] "R version 4.1.2 (2021-11-01)"  

Данные

 dat lt;- structure(list(SUB = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,  2, 2, 2, 2, 2, 2, 2, 2, 2, 2), B = c(1, 0, 0, 1, 1, 1, 0, 1,  1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0), A = c(10, 20, 10,  31, 51, 1, 60, 1, 2, 0, 12, 0, 20, 1, 0, 0, 0, 0, 1, 0, 1, 1,  1), C = c(1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0,  0, 0, 0, 0, 0, 1)), class = "data.frame", row.names = c(NA, -23L ))  

Ответ №4:

Это длинный ответ, но он должен быть довольно гибким.

 library(tidyverse)  cor.by.group.combos lt;- function(.data, groups, vars){  by lt;- gsub(x = rlang::quo_get_expr(enquo(groups)), pattern = "\((.*)?\)", replacement = "\1")[-1]    piv lt;- gsub(x = rlang::quo_get_expr(enquo(vars)), pattern = "\((.*)?\)", replacement = "\1")[-1]    .data %gt;%  group_by(!!!groups) %gt;%  group_split() %gt;%  map(.,  ~pivot_longer(., cols = all_of(piv), names_to = "name", values_to = "val") %gt;%  nest(data = val) %gt;%  full_join(.,.,by = by) %gt;%  filter(name.x != name.y) %gt;%  mutate(test = paste(name.x, "vs",name.y, sep = "."),  grp = paste0(by,!!!groups),  cor = map2_dbl(data.x,data.y, ~cor(unlist(.x), unlist(.y)))) %gt;%  select(test,grp, cor)  ) %gt;%  bind_rows() %gt;%  pivot_wider(names_from = test, values_from = cor) }  cor.by.group.combos(dat, vars(SUB), vars(A, B, C)) #gt; # A tibble: 2 x 7 #gt; grp A.vs.B A.vs.C B.vs.A B.vs.C C.vs.A C.vs.B #gt; lt;chrgt; lt;dblgt; lt;dblgt; lt;dblgt; lt;dblgt; lt;dblgt; lt;dblgt; #gt; 1 SUB1 -0.153 -0.153 -0.153 1 -0.153 1  #gt; 2 SUB2 0.108 0.0461 0.108 0.822 0.0461 0.822  

По сути, то, что мы делаем, — это разбивка данных по группам, а затем применение cor теста к каждой комбинации выбранных переменных. То, как я это настрою, даст некоторые повторяющиеся тесты (например, A. против B и B. против A). Вы могли бы исправить это , используя combn вместо full_join этого, но я не стал тратить время на проработку деталей. Эта функция должна работать, если вы изменяете входные переменные, группирующие переменные и т. Д. Вы также можете применить несколько групп с помощью этого метода.