Jednoczesne Scalanie wielu danych.ramki na liście
Mam listę wielu danych.ramki, które chcę połączyć. Problem polega na tym, że każde dane.ramka różni się pod względem liczby wierszy i kolumn, ale wszystkie mają wspólne kluczowe zmienne(które nazwałem "var1"
i "var2"
w kodzie poniżej). Jeśli dane.ramki były identyczne pod względem kolumn, mogłem tylko rbind
, dla których plyr jest rbind.fill zrobiłoby to zadanie, ale tak nie jest w przypadku tych danych.
Ponieważ polecenie merge
działa tylko na 2 danych.ramki, odwróciłem do Internetu po pomysły. Mam ten z tutaj , który działał idealnie w R 2.7.2, czyli to co miałem wtedy:
merge.rec <- function(.list, ...){
if(length(.list)==1) return(.list[[1]])
Recall(c(list(merge(.list[[1]], .list[[2]], ...)), .list[-(1:2)]), ...)
}
I nazwałbym funkcję tak:
df <- merge.rec(my.list, by.x = c("var1", "var2"),
by.y = c("var1", "var2"), all = T, suffixes=c("", ""))
Ale w każdej wersji R po 2.7.2, w tym 2.11 i 2.12, kod ten nie działa z następującym błędem:
Error in match.names(clabs, names(xi)) :
names do not match previous names
(przypadkowo widzę inne odniesienia do tego błędu gdzie indziej bez rozwiązania).
Czy można to jakoś rozwiązać?5 answers
Kolejne pytanie zadane konkretnie Jak wykonać wiele lewych złączeń przy użyciu dplyr w R . Pytanie zostało oznaczone jako DUPLIKAT tego, więc odpowiadam tutaj, używając 3 przykładowych ramek danych poniżej:
library(dplyr)
x <- data_frame(i = c("a","b","c"), j = 1:3)
y <- data_frame(i = c("b","c","d"), k = 4:6)
z <- data_frame(i = c("c","d","a"), l = 7:9)
Aktualizacja czerwiec 2018 : podzieliłem odpowiedź na trzy sekcje reprezentujące trzy różne sposoby wykonania połączenia. Prawdopodobnie chcesz użyć purrr
, Jeśli używasz już pakietów tidyverse. Dla celów porównania poniżej, znajdziesz podstawową wersję R przy użyciu tego samego przykładowego zestawu danych.
Połącz je z reduce
z pakietu purrr
Pakiet purrr
dostarcza reduce
funkcji o zwięzłej składni:
library(tidyverse)
list(x, y, z) %>% reduce(left_join, by = "i")
# A tibble: 3 x 4
# i j k l
# <chr> <int> <int> <int>
# 1 a 1 NA 9
# 2 b 2 4 NA
# 3 c 3 5 7
Możesz również wykonywać inne złączenia, takie jak full_join
lub inner_join
:
list(x, y, z) %>% reduce(full_join, by = "i")
# A tibble: 4 x 4
# i j k l
# <chr> <int> <int> <int>
# 1 a 1 NA 9
# 2 b 2 4 NA
# 3 c 3 5 7
# 4 d NA 6 8
list(x, y, z) %>% reduce(inner_join, by = "i")
# A tibble: 1 x 4
# i j k l
# <chr> <int> <int> <int>
# 1 c 3 5 7
dplyr::left_join()
z bazą R Reduce()
list(x,y,z) %>%
Reduce(function(dtf1,dtf2) left_join(dtf1,dtf2,by="i"), .)
# i j k l
# 1 a 1 NA 9
# 2 b 2 4 NA
# 3 c 3 5 7
Baza R merge()
z bazą R Reduce()
I dla celów porównania, tutaj jest podstawowa wersja R lewego połączenia
Reduce(function(dtf1, dtf2) merge(dtf1, dtf2, by = "i", all.x = TRUE),
list(x,y,z))
# i j k l
# 1 a 1 NA 9
# 2 b 2 4 NA
# 3 c 3 5 7
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-30 06:33:15
Redukcja sprawia, że jest to dość łatwe:
merged.data.frame = Reduce(function(...) merge(..., all=T), list.of.data.frames)
Oto w pełni przykładowy przykład użycia niektórych przykładowych danych:
set.seed(1)
list.of.data.frames = list(data.frame(x=1:10, a=1:10), data.frame(x=5:14, b=11:20), data.frame(x=sample(20, 10), y=runif(10)))
merged.data.frame = Reduce(function(...) merge(..., all=T), list.of.data.frames)
tail(merged.data.frame)
# x a b y
#12 12 NA 18 NA
#13 13 NA 19 NA
#14 14 NA 20 0.4976992
#15 15 NA NA 0.7176185
#16 16 NA NA 0.3841037
#17 19 NA NA 0.3800352
A oto przykład wykorzystania tych danych do replikacji my.list
:
merged.data.frame = Reduce(function(...) merge(..., by=match.by, all=T), my.list)
merged.data.frame[, 1:12]
# matchname party st district chamber senate1993 name.x v2.x v3.x v4.x senate1994 name.y
#1 ALGIERE 200 RI 026 S NA <NA> NA NA NA NA <NA>
#2 ALVES 100 RI 019 S NA <NA> NA NA NA NA <NA>
#3 BADEAU 100 RI 032 S NA <NA> NA NA NA NA <NA>
Uwaga: wygląda na to, że jest to prawdopodobnie błąd w merge
. Problem polega na tym, że nie ma sprawdzania, czy dodanie sufiksów (aby obsłużyć nakładające się niepasujące nazwy) czyni je unikalnymi. W pewnym momencie używa [.data.frame
, Które robi make.unique
nazwy, powodując, że rbind
do porażka.
# first merge will end up with 'name.x' & 'name.y'
merge(my.list[[1]], my.list[[2]], by=match.by, all=T)
# [1] matchname party st district chamber senate1993 name.x
# [8] votes.year.x senate1994 name.y votes.year.y
#<0 rows> (or 0-length row.names)
# as there is no clash, we retain 'name.x' & 'name.y' and get 'name' again
merge(merge(my.list[[1]], my.list[[2]], by=match.by, all=T), my.list[[3]], by=match.by, all=T)
# [1] matchname party st district chamber senate1993 name.x
# [8] votes.year.x senate1994 name.y votes.year.y senate1995 name votes.year
#<0 rows> (or 0-length row.names)
# the next merge will fail as 'name' will get renamed to a pre-existing field.
Najprostszym sposobem naprawienia jest nie opuszczanie nazwy pola dla duplikatów pól (których jest tu wiele) aż do merge
. Eg:
my.list2 = Map(function(x, i) setNames(x, ifelse(names(x) %in% match.by,
names(x), sprintf('%s.%d', names(x), i))), my.list, seq_along(my.list))
The merge
/Reduce
wtedy będzie działać dobrze.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-22 13:41:15
Możesz to zrobić używając merge_all
w pakiecie reshape
. Możesz przekazać parametry merge
używając argumentu ...
reshape::merge_all(list_of_dataframes, ...)
Oto doskonałe źródło informacji na temat różnych metod łączenia ramek danych.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-08 09:15:58
Możesz użyć rekurencji, aby to zrobić. Nie zweryfikowałem tego, co następuje, ale powinno dać ci właściwy pomysł:
MergeListOfDf = function( data , ... )
{
if ( length( data ) == 2 )
{
return( merge( data[[ 1 ]] , data[[ 2 ]] , ... ) )
}
return( merge( MergeListOfDf( data[ -1 ] , ... ) , data[[ 1 ]] , ... ) )
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-11 15:19:05
Wykorzystam ponownie przykład danych z @ PaulRougieux
x <- data_frame(i = c("a","b","c"), j = 1:3)
y <- data_frame(i = c("b","c","d"), k = 4:6)
z <- data_frame(i = c("c","d","a"), l = 7:9)
Oto krótkie i słodkie rozwiązanie za pomocą purrr
i tidyr
library(tidyverse)
list(x, y, z) %>%
map_df(gather, key=key, value=value, -i) %>%
spread(key, value)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-28 10:59:54