Dados dois quadros de dados:
df1 = data.frame(CustomerId = c(1:6), Product = c(rep("Toaster", 3), rep("Radio", 3)))
df2 = data.frame(CustomerId = c(2, 4, 6), State = c(rep("Alabama", 2), rep("Ohio", 1)))
df1
# CustomerId Product
# 1 Toaster
# 2 Toaster
# 3 Toaster
# 4 Radio
# 5 Radio
# 6 Radio
df2
# CustomerId State
# 2 Alabama
# 4 Alabama
# 6 Ohio
Como faço para ingressar no estilo de banco de dados, ou seja, estilo sql ? Ou seja, como obtenho:
- Uma junção interna de
df1
edf2
:
Retorna apenas as linhas nas quais a tabela esquerda possui chaves correspondentes na tabela direita. - Uma junção externa de
df1
edf2
:
Retorna todas as linhas das duas tabelas, junta os registros da esquerda que possuem chaves correspondentes na tabela da direita. - Uma junção externa esquerda (ou simplesmente junção esquerda) de
df1
edf2
Retorna todas as linhas da tabela esquerda e todas as linhas com chaves correspondentes da tabela direita. - Uma junção externa direita de
df1
edf2
Retorne todas as linhas da tabela direita e todas as linhas com chaves correspondentes da tabela esquerda.
Crédito extra:
Como posso fazer uma instrução de seleção de estilo SQL?
Respostas:
Usando a
merge
função e seus parâmetros opcionais:Junção interna:
merge(df1, df2)
funcionará para esses exemplos porque R une automaticamente os quadros por nomes de variáveis comuns, mas você provavelmente desejaria especificarmerge(df1, df2, by = "CustomerId")
para garantir que estava correspondendo apenas aos campos desejados. Você também pode usar osparâmetrosby.x
eby.y
se as variáveis correspondentes tiverem nomes diferentes nos diferentes quadros de dados.Junção externa:
merge(x = df1, y = df2, by = "CustomerId", all = TRUE)
Exterior esquerdo:
merge(x = df1, y = df2, by = "CustomerId", all.x = TRUE)
Exterior direito:
merge(x = df1, y = df2, by = "CustomerId", all.y = TRUE)
Junção cruzada:
merge(x = df1, y = df2, by = NULL)
Assim como na junção interna, você provavelmente desejaria passar explicitamente "CustomerId" para R como a variável correspondente.Eu acho que quase sempre é melhor declarar explicitamente os identificadores nos quais você deseja mesclar; é mais seguro se os dados de entrada. quadros forem alterados inesperadamente e mais fáceis de serem lidos posteriormente.Você pode mesclar em várias colunas, dando
by
um vetor, por exemploby = c("CustomerId", "OrderId")
,.Se os nomes das colunas a serem mesclados não forem os mesmos, é possível especificar, por exemplo,
by.x = "CustomerId_in_df1", by.y = "CustomerId_in_df2"
ondeCustomerId_in_df1
é o nome da coluna no primeiro quadro de dados eCustomerId_in_df2
o nome da coluna no segundo quadro de dados. (Esses também podem ser vetores se você precisar mesclar em várias colunas.)fonte
data.table
pacote - esse é um conjunto totalmente novo de sintaxe de junção, mas é radicalmente mais rápido do que qualquer coisa que estamos falando aqui.merge(x=df1,y=df2, by.x=c("x_col1","x_col2"), by.y=c("y_col1","y_col2"))
data.table
agora, com a mesma função apenas mais rapidamente.Eu recomendaria verificar o pacote sqldf do Gabor Grothendieck , que permite expressar essas operações no SQL.
Acho que a sintaxe SQL é mais simples e mais natural do que seu equivalente em R (mas isso pode refletir apenas meu viés de RDBMS).
Consulte o sqldf GitHub do Gabor para obter mais informações sobre junções.
fonte
Existe a abordagem data.table para uma junção interna, que consome muito tempo e memória (e necessária para alguns data.frames maiores):
merge
também funciona em data.tables (como é genérico e chamamerge.data.table
)data.table documentado no stackoverflow:
Como executar uma operação de mesclagem data.table
Traduzindo junções SQL em chaves estrangeiras para R sintaxe data.table
Alternativas eficientes para mesclar para dados maiores.frames R
Como fazer uma junção externa esquerda básica com data.table em R?
Outra opção é a
join
função encontrada no pacote plyrOpções para
type
:inner
,left
,right
,full
.De
?join
: ao contráriomerge
, [join
] preserva a ordem de x, independentemente do tipo de junção usado.fonte
plyr::join
. Microbenchmarking indica que ele executa cerca de 3 vezes mais rápido quemerge
.data.table
é muito mais rápido que ambos. Também há um ótimo suporte no SO, não vejo muitos redatores de pacotes respondendo perguntas aqui com a mesma frequência que odata.table
gravador ou os colaboradores.data.table
sintaxe para mesclar uma lista de quadros de dados ?nomatch = 0L
nesse caso.Você também pode fazer junções usando o incrível pacote dplyr de Hadley Wickham .
Mutando junções: adicione colunas ao df1 usando correspondências no df2
Filtrando junções: filtre as linhas no df1, não modifique as colunas
fonte
CustomerId
para numérico? Não vejo nenhuma menção na documentação (para ambosplyr
edplyr
) sobre esse tipo de restrição. Seu código funcionaria incorretamente se a coluna de mesclagem fosse docharacter
tipo (especialmente interessadaplyr
)? Estou esquecendo de algo?Existem alguns bons exemplos de como fazer isso no R Wiki . Vou roubar alguns aqui:
Método de mesclagem
Como suas chaves são nomeadas da mesma forma, a maneira mais curta de fazer uma junção interna é mesclar ():
uma junção interna completa (todos os registros de ambas as tabelas) pode ser criada com a palavra-chave "all":
uma junção externa esquerda de df1 e df2:
uma junção externa direita de df1 e df2:
você pode virar, bater e esfregar para baixo para obter as outras duas junções externas que você perguntou :)
Método Subscrito
Uma junção externa esquerda com df1 à esquerda usando um método subscrito seria:
A outra combinação de junções externas pode ser criada através da junção do exemplo do subscrito da junção externa esquerda. (sim, eu sei que é o equivalente a dizer "vou deixar isso como um exercício para o leitor ...")
fonte
Novo em 2014:
Especialmente se você também estiver interessado em manipulação de dados em geral (incluindo classificação, filtragem, subconjunto, resumo etc.), você definitivamente deve dar uma olhada
dplyr
, que vem com uma variedade de funções, todas projetadas para facilitar seu trabalho especificamente com quadros de dados e alguns outros tipos de banco de dados. Ele ainda oferece uma interface SQL bastante elaborada e até uma função para converter (a maioria) código SQL diretamente em R.As quatro funções relacionadas à junção no pacote dplyr são (entre aspas):
inner_join(x, y, by = NULL, copy = FALSE, ...)
: retorna todas as linhas de x onde existem valores correspondentes em y e todas as colunas de x e yleft_join(x, y, by = NULL, copy = FALSE, ...)
: retorna todas as linhas de x e todas as colunas de x e ysemi_join(x, y, by = NULL, copy = FALSE, ...)
: retorna todas as linhas de x onde existem valores correspondentes em y, mantendo apenas as colunas de x.anti_join(x, y, by = NULL, copy = FALSE, ...)
: retorna todas as linhas de x onde não há valores correspondentes em y, mantendo apenas as colunas de xEstá tudo aqui em grandes detalhes.
A seleção de colunas pode ser feita por
select(df,"column")
. Se isso não for suficiente para você, existe asql()
função na qual você pode inserir o código SQL como está, e ele fará a operação que você especificou exatamente como você estava escrevendo em R o tempo todo (para obter mais informações, consulte à vinheta dplyr / database ). Por exemplo, se aplicado corretamente,sql("SELECT * FROM hflights")
selecionará todas as colunas da tabela dplyr "hflights" (um "tbl").fonte
Atualize os métodos data.table para ingressar em conjuntos de dados. Veja abaixo exemplos para cada tipo de associação. Existem dois métodos, um
[.data.table
ao passar a segunda data.table como o primeiro argumento para subconjunto, outra maneira é usar amerge
função que despacha para o método data.table mais rápido.Testes abaixo do benchmark base R, sqldf, dplyr e data.table.
O benchmark testa conjuntos de dados não codificados / não indexados. O benchmark é realizado em conjuntos de dados de 50M-1 linhas, existem valores comuns de 50M-2 na coluna de junção para que cada cenário (interno, esquerdo, direito, cheio) possa ser testado e a junção ainda não seja trivial para executar. É o tipo de junção que enfatiza bem os algoritmos de junção. Horários são de
sqldf:0.4.11
,dplyr:0.7.8
,data.table:1.12.0
.Esteja ciente de que existem outros tipos de junções que você pode executar usando
data.table
:- atualização na junção - se você deseja pesquisar valores de outra tabela para sua tabela principal
- agregado na junção - se você deseja agregar na chave da qual você está ingressando, não possui para materializar todos se unem resultados
- sobreposição juntar - se você deseja mesclar por faixas
- rolando juntar - se você quiser mesclagem para ser capaz de corresponder aos valores da precedente / seguinte linhas rolando para a frente ou para trás
- não equi juntar - se a sua a condição de junção não é igual
Código a reproduzir:
fonte
on =
também?on
argmerge.data.table
osort = TRUE
argumento padrão existe , que adiciona uma chave durante a mesclagem e a deixa lá no resultado. Isso é algo a ser observado, especialmente se você estiver tentando evitar definir teclas.data.table
, o que você quer dizer? Por favor você pode ser mais especifico.O dplyr desde o 0.4 implementou todas essas junções
outer_join
, inclusive , mas vale a pena notar que, nos primeiros lançamentos anteriores ao 0.4, não costumava oferecerouter_join
, e como resultado, havia muito código de usuário de solução de hacky muito ruim flutuando por um bom tempo depois (você ainda pode encontrar esse código no SO, Kaggle responde, github desse período. Portanto, essa resposta ainda serve a um propósito útil.)Destaques da versão relacionada à junção :
v0.5 (6/2016)
v0.4.0 (1/2015)
v0.3 (10/2014)
v0.2 (5/2014)
v0.1.3 (4/2014)
Soluções alternativas pelos comentários de hadley nessa edição:
fonte
dplyr
sintaxe, a mudança delazyeval
pararlang
back - end quebrou um monte de código para mim, o que me levou a aprender maisdata.table
, e agora uso principalmentedata.table
.)plyr
/dplyr
/data.table
/ tidyverse depende enormemente em que ano nós começamos, e que (embrionárias) afirmam os pacotes estavam em volta, em seguida, ao contrário de agora ...Ao unir dois quadros de dados com ~ 1 milhão de linhas cada, um com 2 colunas e outro com ~ 20, surpreendentemente achei
merge(..., all.x = TRUE, all.y = TRUE)
mais rápidodplyr::full_join()
. Isso é com o dplyr v0.4A mesclagem leva ~ 17 segundos, full_join leva ~ 65 segundos.
Alguns alimentos para, no entanto, uma vez que geralmente eu dplyr para tarefas de manipulação.
fonte
No caso de uma junção esquerda com uma
0..*:0..1
cardinalidade ou uma junção direita com uma0..1:0..*
cardinalidade, é possível atribuir no local as colunas unilaterais do marceneiro (a0..1
tabela) diretamente ao participante (a0..*
tabela) e, assim, evitar a criação de uma tabela de dados inteiramente nova. Isso requer a correspondência das colunas-chave do associado no marceneiro e a indexação + ordenando as linhas do marceneiro de acordo para a atribuição.Se a chave for uma única coluna, podemos usar uma única chamada
match()
para fazer a correspondência. Este é o caso que abordarei nesta resposta.Aqui está um exemplo baseado no OP, exceto que eu adicionei uma linha extra
df2
com um ID 7 para testar o caso de uma chave não correspondente no marceneiro. Isso efetivamente édf1
deixado à esquerdadf2
:No código acima, eu codifiquei uma suposição de que a coluna chave é a primeira coluna de ambas as tabelas de entrada. Eu argumentaria que, em geral, essa não é uma suposição irracional, pois, se você tiver um data.frame com uma coluna-chave, seria estranho se ele não tivesse sido configurado como a primeira coluna do data.frame de desde o início. E você sempre pode reordenar as colunas para fazer isso. Uma conseqüência vantajosa dessa suposição é que o nome da coluna-chave não precisa ser codificado, embora eu suponha que esteja apenas substituindo uma suposição por outra. A concisão é outra vantagem da indexação inteira, além da velocidade. Nos benchmarks abaixo, alterarei a implementação para usar a indexação de nomes de string para corresponder às implementações concorrentes.
Eu acho que essa é uma solução particularmente apropriada se você tiver várias tabelas que deseja deixar ingressar em uma única tabela grande. Recriar repetidamente a tabela inteira para cada mesclagem seria desnecessário e ineficiente.
Por outro lado, se você precisar que o participante permaneça inalterado nessa operação por qualquer motivo, essa solução não poderá ser usada, pois modifica o participante diretamente. Embora, nesse caso, você possa simplesmente fazer uma cópia e executar as atribuições no local da cópia.
Como uma observação lateral, examinei brevemente possíveis soluções correspondentes para chaves de várias colunas. Infelizmente, as únicas soluções correspondentes que encontrei foram:
match(interaction(df1$a,df1$b),interaction(df2$a,df2$b))
, ou a mesma idéia compaste()
.outer(df1$a,df2$a,`==`) & outer(df1$b,df2$b,`==`)
.merge()
e funções de mesclagem baseadas em pacotes equivalentes, que sempre alocam uma nova tabela para retornar o resultado mesclado e, portanto, não são adequadas para uma solução baseada em atribuição no local.Por exemplo, consulte Combinando várias colunas em diferentes quadros de dados e obtendo outra coluna como resultado , combine duas colunas com duas outras colunas , Correspondendo a várias colunas , e o embuste dessa pergunta em que eu originalmente criei a solução local, Combine duas tramas de dados com um número diferente de fileiras em R .
avaliação comparativa
Decidi fazer meu próprio benchmarking para ver como a abordagem de atribuição no local se compara às outras soluções que foram oferecidas nesta pergunta.
Código de teste:
Aqui está uma referência do exemplo baseado no OP que demonstrei anteriormente:
Aqui, eu comparo dados de entrada aleatórios, tentando diferentes escalas e diferentes padrões de sobreposição de chaves entre as duas tabelas de entrada. Essa referência ainda está restrita ao caso de uma chave inteira de coluna única. Além disso, para garantir que a solução local funcione para as junções esquerda e direita das mesmas tabelas, todos os dados de teste aleatório usam
0..1:0..1
cardinalidade. Isso é implementado por amostragem sem substituição da coluna-chave do primeiro data.frame ao gerar a coluna-chave do segundo data.frame.Eu escrevi um código para criar gráficos de log-log dos resultados acima. Eu gerei um gráfico separado para cada porcentagem de sobreposição. É um pouco confuso, mas eu gosto de ter todos os tipos de solução e tipos de junção representados no mesmo gráfico.
Usei a interpolação de splines para mostrar uma curva suave para cada combinação de solução / tipo de junção, desenhada com símbolos individuais de pch. O tipo de junção é capturado pelo símbolo pch, usando um ponto para os colchetes interno, esquerdo e direito para esquerda e direita e um diamante para cheio. O tipo de solução é capturado pela cor, como mostrado na legenda.
Aqui está uma segunda referência em larga escala, mais pesada, com relação ao número e tipos de colunas-chave, além de cardinalidade. Para esse benchmark, uso três colunas principais: um caractere, um inteiro e um lógico, sem restrições de cardinalidade (ou seja,
0..*:0..*
). (Em geral, não é aconselhável definir colunas-chave com valores duplos ou complexos devido a complicações de comparação de ponto flutuante e, basicamente, ninguém nunca usa o tipo bruto, muito menos para colunas-chave, por isso não incluí esses tipos na chave Além disso, para fins de informação, tentei inicialmente usar quatro colunas-chave incluindo uma coluna-chave POSIXct, mas o tipo POSIXct não funcionou bem com asqldf.indexed
solução por algum motivo, possivelmente devido a anomalias de comparação de ponto flutuante, então eu removido.)As plotagens resultantes, usando o mesmo código de plotagem fornecido acima:
fonte
merge
função, podemos selecionar a variável da tabela esquerda ou da direita, da mesma maneira que todos conhecemos com a instrução select no SQL (EX: selecione a. * ... ou selecione b. * Em .....)Temos que adicionar código extra que será subconjunto da tabela recém-ingressada.
SQL: -
select a.* from df1 a inner join df2 b on a.CustomerId=b.CustomerId
R: -
merge(df1, df2, by.x = "CustomerId", by.y = "CustomerId")[,names(df1)]
Da mesma maneira
SQL: -
select b.* from df1 a inner join df2 b on a.CustomerId=b.CustomerId
R: -
merge(df1, df2, by.x = "CustomerId", by.y = "CustomerId")[,names(df2)]
fonte
Para uma junção interna em todas as colunas, você também pode usar a
fintersect
partir do pacote data.table ouintersect
do pacote dplyr como uma alternativa para,merge
sem especificar asby
colunas. isso fornecerá as linhas iguais entre dois quadros de dados:Dados de exemplo:
fonte
Atualizar associação. Uma outra junção importante no estilo SQL é uma " junção de atualização ", na qual as colunas em uma tabela são atualizadas (ou criadas) usando outra tabela.
Modificando as tabelas de exemplo do OP ...
Suponha que desejemos adicionar o estado do cliente de
cust
à tabela de comprassales
, ignorando a coluna do ano. Com a base R, podemos identificar linhas correspondentes e copiar valores sobre:Como pode ser visto aqui,
match
seleciona a primeira linha correspondente da tabela do cliente.Atualizar junção com várias colunas.A abordagem acima funciona bem quando ingressamos em apenas uma coluna e estamos satisfeitos com a primeira partida. Suponha que desejamos que o ano de medição na tabela de clientes corresponda ao ano de venda.
Como a resposta de @ bgoldst menciona,
match
cominteraction
pode ser uma opção para este caso. Mais diretamente, pode-se usar data.table:Junção de atualização contínua. Como alternativa, convém tomar o último estado em que o cliente foi encontrado:
Os três exemplos acima se concentram na criação / adição de uma nova coluna. Consulte as perguntas frequentes relacionadas ao R para obter um exemplo de atualização / modificação de uma coluna existente.
fonte