Свернуть и пересечь фреймы данных

У меня есть два data.frame, которые имеют 3 столбца: 1. id - уникальный ключ

  1. target - уникальные значения, разделенные точкой с запятой

  2. source — одинаковые для каждого кадра данных, но разные для двух data.frame.

Вот смоделированные данные:

set.seed(1)
df.1 <- data.frame(id=LETTERS[sample(length(LETTERS),10,replace=F)],
                   target=sapply(1:10,function(x) paste(LETTERS[sample(length(LETTERS),5,replace=F)],collapse=";")),
                   source="A",stringsAsFactors=F)

df.2 <- data.frame(id=LETTERS[sample(length(LETTERS),5,replace=F)],
                   target=sapply(1:5,function(x) paste(LETTERS[sample(length(LETTERS),5,replace=F)],collapse=";")),
                   source="B",stringsAsFactors=F)

Я ищу функцию, которая объединит два data.frame вместе и создаст 3 столбца:

1.intersected.targets - уникальные значения, разделенные точкой с запятой, которые пересекаются между двумя data.frame

2.source1.targets - цели, которые уникальны для первого data.frame

3.source2.targets - цели, которые уникальны для второго data.frame

Таким образом, для приведенного выше примера результирующее число data.frame будет таким:

> res.df
   id intersected.targets sourceA.targets sourceB.targets
1   G                  NA       F;E;Q;I;X            <NA>
2   J                  NA       M;R;X;I;Y            <NA>
3   N                  NA       Y;F;P;C;Z            <NA>
4   U                  NA       K;A;J;U;H            <NA>
5   E                  NA       M;O;L;E;S            <NA>
6   S                  NA       R;T;C;Q;J            <NA>
7   W                  NA       V;Q;S;M;L            <NA>
8   M                  NA       U;A;L;Q;P            <NA>
9   B                  NA       C;H;M;P;I            <NA>
10  X                  NA            <NA>       G;L;S;B;T
11  H                  NA            <NA>       I;U;Z;H;K
12  Y                  NA            <NA>       L;R;J;H;Q
13  O                  NA            <NA>       F;R;C;Z;D
14  L                  V       M;K;F;B       X;J;R;Y

person dan    schedule 16.08.2016    source источник
comment
Вы можете начать с library(data.table) ; dcast(rbind(setDT(df.1), setDT(df.2)), id ~ source, value.var = "target"). Не уверен, что вы хотите в столбце intersected.targets, поскольку вы не указали его в желаемом выводе.   -  person David Arenburg    schedule 16.08.2016
comment
Вам не нужна эта модификация (не говоря уже о том, что ваш код не работал), потому что у вас уже есть общий V там   -  person David Arenburg    schedule 16.08.2016
comment
Верно, извините за это. Отредактировано соответственно   -  person dan    schedule 16.08.2016
comment
Я предполагаю, что @DavidArenburg только временно удалил свой ответ и редактирует его, и если это так, я собирался предложить ему продолжить использование методов с данными, используя преобразование в столбцы списка для исходных столбцов, а затем запустить setdiff, by = id.   -  person IRTFM    schedule 16.08.2016
comment
@ 42- Нет, у меня нет на это времени, вы можете опубликовать решение, если оно есть.   -  person David Arenburg    schedule 16.08.2016
comment
Крысы, я дошел до вас, используя базовые методы, но отчасти надеялся, что вы продвинулись дальше, и я могу сдаться. (Я думаю, что вы намного лучше разбираетесь в data.table, чем я.)   -  person IRTFM    schedule 16.08.2016
comment
concat строка, символы таблицы, подмножество ›1, строка соединения?   -  person shayaa    schedule 16.08.2016


Ответы (2)


Это продолжение удаленного ответа Дэвида Аренберга, который научил меня понятию создания столбца списка в таблице данных. Я не знал, как правильно реализовать свою идею использования setdiff построчно, но в конце концов после нескольких поисков нашел ответ Фрэнка, который это делает. Вот (частичный) ответ Дэвида:

===== Вот возможное решение для другого начального числа, которое имеет более одного пересечения и более одной буквы в одном пересечении.

#Generating Data

set.seed(123)
df.1 <- data.frame(id=LETTERS[sample(length(LETTERS),10,replace=F)],
                   target=sapply(1:10,function(x) paste(LETTERS[sample(length(LETTERS),5,
                                                                replace=F)],collapse=";")),
                   source="A",stringsAsFactors=F)

df.2 <- data.frame(id=LETTERS[sample(length(LETTERS),5, replace=F)],
                   target=sapply(1:5,function(x) paste(LETTERS[sample(length(LETTERS),5, 
                                                               replace=F)],collapse=";")),
                   source="B",stringsAsFactors=F)
#Solution

library(data.table) 
library(stringi)
res <- dcast(rbind(setDT(df.1), setDT(df.2)), id ~ source, value.var = "target")
res[!is.na(A) & !is.na(B), intersected.targets := 
                             stri_extract_all(A, regex = gsub(";", "|", B, fixed = TRUE))]
res

==========================

Поэтому я использовал его листинговый код, чтобы создать столбцы A2 и B2, которые являются списковой версией A и B.

res[ , A2 := stri_extract_all(A, regex = "[[:alpha:]]") ]
 res[ , B2 := stri_extract_all(B, regex = "[[:alpha:]]") ]

Затем использовал Map() для построчного setdiff:

res[, SourceA := Map( setdiff, A2, intersected.targets)]
res[, SourceB := Map( setdiff, B, intersected.targets)]
 res
#-------------------------------
    id         A         B intersected.targets        A2        B2   SourceA   SourceB
 1:  A M;S;F;H;X        NA                NULL M,S,F,H,X        NA M,S,F,H,X        NA
 2:  C        NA T;P;R;A;K                NULL        NA T,P,R,A,K        NA T,P,R,A,K
 3:  G        NA G;Q;K;S;C                NULL        NA G,Q,K,S,C        NA G,Q,K,S,C
 4:  H Y;L;Q;N;C        NA                NULL Y,L,Q,N,C        NA Y,L,Q,N,C        NA
 5:  J X;R;P;W;O F;J;O;I;C                   O X,R,P,W,O F,J,O,I,C   X,R,P,W   F,J,I,C
 6:  K D;K;J;I;Z        NA                NULL D,K,J,I,Z        NA D,K,J,I,Z        NA
 7:  Q D;F;L;G;S        NA                NULL D,F,L,G,S        NA D,F,L,G,S        NA
 8:  R        NA L;U;T;S;J                NULL        NA L,U,T,S,J        NA L,U,T,S,J
 9:  T X;G;B;H;U        NA                NULL X,G,B,H,U        NA X,G,B,H,U        NA
10:  U S;N;O;G;D        NA                NULL S,N,O,G,D        NA S,N,O,G,D        NA
11:  W Z;W;Q;S;A        NA                NULL Z,W,Q,S,A        NA Z,W,Q,S,A        NA
12:  X B;L;T;C;M        NA                NULL B,L,T,C,M        NA B,L,T,C,M        NA
13:  Z F;D;S;U;I L;Y;V;U;D                 D,U F,D,S,U,I L,Y,V,U,D     F,S,I     L,Y,V

Я оставлю уборку как студенческое упражнение.

person IRTFM    schedule 16.08.2016

Боль в заднице в этом типе очистки данных, как упоминает @ 42-, заключается в удалении кадров данных из списков.

library(dplyr)
library(stringr)
df <- full_join(df.1, df.2) %>% 
  spread(source, target)  %>%
  mutate(intersect_targets = str_c(A,B,sep = ";"))

df[,4][!is.na(df[,4])] <- names(do.call("c",lapply(df$intersect_targets, function(x) 
which(table(str_split(x, ";"))>1))))

a <- sapply(seq(nrow(df)), function(x) {
str_split(df[x,2:3],";")
})

sa <-  do.call("c",lapply(mapply(setdiff,a[1,], a[2,]),paste0, collapse = ","))
sb <- do.call("c",lapply(mapply(setdiff,a[2,], a[1,]), paste0, collapse = ","))

df[,2:3] <-cbind(sa,sb)

 head(df)
  id         A         B intersect_targets
1  B C,H,M,P,I        NA              <NA>
2  E M,O,L,E,S        NA              <NA>
3  G F,E,Q,I,X        NA              <NA>
4  H        NA I,U,Z,H,K              <NA>
5  J M,R,X,I,Y        NA              <NA>
6  L   M,K,F,B   X,J,R,Y                 V
person shayaa    schedule 16.08.2016
comment
Не похоже, что вы удалили пересекающиеся элементы из входных наборов. - person IRTFM; 16.08.2016
comment
Возможно, мы могли бы сделать это в версии без списка, например: A <- gsub( paste( ";*, intersect_targets), "", A). Как-то обидно потратить час на @AM на вопрос, а потом не получить одобрения, а? - person IRTFM; 17.08.2016
comment
У тебя есть мой. Только сейчас не было возможности прочитать от начала до конца. - person shayaa; 17.08.2016