Como especificar nomes de colunas para xey ao juntar em dplyr?

91

Tenho dois frames de dados que desejo unir usando dplyr. Um é um quadro de dados contendo os primeiros nomes.

test_data <- data.frame(first_name = c("john", "bill", "madison", "abby", "zzz"),
                        stringsAsFactors = FALSE)

O outro quadro de dados contém uma versão limpa do corpus de nomes de Kantrowitz, identificando o gênero. Aqui está um exemplo mínimo:

kantrowitz <- structure(list(name = c("john", "bill", "madison", "abby", "thomas"), gender = c("M", "either", "M", "either", "M")), .Names = c("name", "gender"), row.names = c(NA, 5L), class = c("tbl_df", "tbl", "data.frame"))

Eu quero essencialmente pesquisar o gênero do nome na test_datatabela usando a kantrowitztabela. Como vou abstrair isso em uma função encode_gender, não saberei o nome da coluna no conjunto de dados que será usado e, portanto, não posso garantir que será name, como em kantrowitz$name.

Na base, RI faria a mesclagem desta forma:

merge(test_data, kantrowitz, by.x = "first_names", by.y = "name", all.x = TRUE)

Isso retorna a saída correta:

  first_name gender
1       abby either
2       bill either
3       john      M
4    madison      M
5        zzz   <NA>

Mas eu quero fazer isso no dplyr porque estou usando esse pacote para todas as minhas outras manipulações de dados. A byopção dplyr para as várias *_joinfunções me permite especificar apenas um nome de coluna, mas preciso especificar dois. Estou procurando algo assim:

library(dplyr)
# either
left_join(test_data, kantrowitz, by.x = "first_name", by.y = "name")
# or
left_join(test_data, kantrowitz, by = c("first_name", "name"))

Qual é a forma de realizar esse tipo de junção usando dplyr?

(Não importa que o corpus de Kantrowitz seja uma maneira ruim de identificar o gênero. Estou trabalhando em uma implementação melhor, mas quero fazer isso funcionar primeiro.)

Lincoln Mullen
fonte
3
Você não pode atualmente, mas está na lista de tarefas
hadley

Respostas:

153

Este recurso foi adicionado no dplyr v0.3. Agora você pode passar um vetor de caractere nomeado para o byargumento em left_join(e outras funções de junção) para especificar quais colunas juntar em cada quadro de dados. Com o exemplo dado na pergunta original, o código seria:

left_join(test_data, kantrowitz, by = c("first_name" = "name"))
Lincoln Mullen
fonte
13
editar Isso funciona no caso geral também left_join(data_a, data_b, by = c("a.first" = "b.first", "a.second" = "b.second", "a.third" = "b.third")):?
davidski
O by =é opcional. Você pode fazerleft_join(test_data, kantrowitz, c("first_name" = "name"))
Pranay Aryal
11
Isso é verdade para qualquer argumento para uma função. Mas geralmente acho melhor ser explícito usando argumentos nomeados em vez de correspondência de posição neste caso.
Lincoln Mullen
5

Esta é mais uma solução alternativa do que uma solução real. Você pode criar um novo objeto test_datacom outro nome de coluna:

left_join("names<-"(test_data, "name"), kantrowitz, by = "name")

     name gender
1    john      M
2    bill either
3 madison      M
4    abby either
5     zzz   <NA>
Sven Hohenstein
fonte
Renomear induz uma cópia, eu acho, que pode ser a maneira que o dplyr evita e obriga você a fazer isso.
joran
2
No 0.1.2 você pelo menos será capaz de fazer select(test_data, first_name = name)e isso só fará uma cópia superficial.
hadley
1
Use data.table::setnames?
Hugh de
2
a solução select (test_data, first_name = name) não funcionava em junho de 2014
userJT