Разверните фрейм данных в комбинации пар строк
У меня есть кадр данных, который содержит столбец идентификатор/ключ, за которым следуют несколько столбцов столбцов значений.
Я хочу расширить столбец данных, взяв уникальные пары записей в ключевом столбце в качестве новых строк и преобразуя столбцы значений, используя двоичные операции над записями из соответствующих строк.
например.
> Test_data
SYS dE_water_free dE_water_periodic dE_membrane_periodic RTlogKi
1 4NTJ_D294N -56.542 -56.642 NA -0.9629731
2 4NTJ_wild -171.031 -162.030 NA -0.8877264
3 4PXZ_D294N -53.430 -50.810 NA -1.1301124
4 4PXZ_wild -59.990 -57.320 NA -1.2318835
5 4PY0_D294N -77.040 -72.880 NA -1.1351579
6 4PY0_wild -79.080 -74.950 NA -1.2297302
Некоторые из столбцов могут содержать или не содержать отсутствующие значения.
то, что я хотел бы, это взять каждую пару записей SYS, например. SYS1 SYS2 и вычислить двоичную операцию в соответствующих строках значений
Например. SYS1 SYS2 dE_water_free (SYS == SYS1) -dE_water_free (SYS == SYS2)... и т.д.
SYS1 SYS2 dE_water_free dE_water_periodic ...etc.
1 4NTJ_D294N 4NTJ_wild 114.489 105.610
2 4NTJ_D294N 4PXZ_D294N -3.112 5.832
... etc.
Я могу использовать функцию combn()
, чтобы получить массив пар из столбца SYSTEM, чтобы сформировать записи в SYS1 и SYS2, но я не уверен, как использовать его для создания нового фрейма данных...
Я знаю, что одним из вариантов было бы использовать что-то вроде mapply и строить каждый столбец индивидуально вручную, а затем вставлять их все в новый фрейм данных, но похоже, что он будет klunky и медленным, и должна быть более автоматическая функция для этого, например, изменить форму, слить или переделать... но я не могу понять, как это работает.
Ответы
Ответ 1
Ваш combn
был хорошим способом. Попробуйте следующее:
combos<-combn(Test_data$SYS,2)
water<-combn(Test_data$dE_water_free,2,FUN=function(x) x[1]-x[2])
data.frame(SYS1=combos[1,],SYS2=combos[2,],water,stringsAsFactors=FALSE)
# SYS1 SYS2 water
#1 4NTJ_D294N 4NTJ_wild 114.489
#2 4NTJ_D294N 4PXZ_D294N -3.112
#3 4NTJ_D294N 4PXZ_wild 3.448
#4 4NTJ_D294N 4PY0_D294N 20.498
#5 4NTJ_D294N 4PY0_wild 22.538
........
Ответ 2
outer
хорошо подходит для такого типа проблем:
de_wf <- with(Test_data, setNames(dE_water_free, SYS))
outer(de_wf, de_wf, `-`)
дает:
4NTJ_D294N 4NTJ_wild 4PXZ_D294N 4PXZ_wild 4PY0_D294N 4PY0_wild
4NTJ_D294N 0.000 114.489 -3.112 3.448 20.498 22.538
4NTJ_wild -114.489 0.000 -117.601 -111.041 -93.991 -91.951
4PXZ_D294N 3.112 117.601 0.000 6.560 23.610 25.650
4PXZ_wild -3.448 111.041 -6.560 0.000 17.050 19.090
4PY0_D294N -20.498 93.991 -23.610 -17.050 0.000 2.040
4PY0_wild -22.538 91.951 -25.650 -19.090 -2.040 0.000
Ответ 3
Вот два решения, которые принимают перекрестное произведение/объединение данных с самим собой.
В базе R я бы рассмотрел outer
:
diffmat <- with(Test_data,outer(dE_water_free,dE_water_free,`-`))
dimnames(diffmat) <- with(Test_data,list(SYS,SYS))
Если вам не нужен результат в матрице,
diffdf <- with(Test_data,data.frame(
SYS1=SYS,
SYS2=rep(SYS,each=length(SYS)),
diff=c(diffmat)
))
С data.table
, я бы использовал @JanGorecki CJ.dt
функцию
require(data.table)
setDT(Test_data)
res <- CJ.dt(Test_data,Test_data)[,`:=`(
freediff = dE_water_free-i.dE_water_free,
perdiff = dE_water_periodic-i.dE_water_periodic
)]
Ответ 4
Решение Frank выглядит намного проще и проще. Но здесь другой подход слияния.
# Set Up
Test.data <- data.frame(
Col1 = c(1,1,1,1,1,1),
SYS = c("4NTJ_D294N",'4NTJ_wild',"4PXZ_D294N","4PXZ_wild","4PY0_D294N","4PY0_wild"),
dE_water_free = c(-56.542,-171.031,-53.43,-59.99,-77.04,-79.08)
)
Новая идея, полагающаяся на dplyr
library("dplyr")
nuDat <- dplyr::left_join(
dplyr::select(Test.data, Col1, SYS1 = SYS, dE_water_free1 = dE_water_free),
dplyr::select(Test.data, Col1, SYS2 = SYS, dE_water_free2 = dE_water_free),
by = "Col1"
) %>%
dplyr::mutate(
dE_water_free = dE_water_free1 - dE_water_free2
) %>%
dplyr::filter(SYS1 != SYS2) %>%
dplyr::select(
SYS1, SYS2, dE_water_free
)