Solucionado (ver solução)
Solucionado
(ver solução)
8
respostas

iterar com dois dataframes

Quebrei a cabeça sozinho bastante antes de criar este tópico aqui.

Tenho dois dataframes do pandas. Um deles, o df1 tem as colunas id e nome, e o outro, df2, só tem o id. Ambos se referem à mesma base de usuarios.

Gostaria de aproveitar os dados de nome em df1 e completar em df2. Tipo iterar cada linha do df1, buscando se a id está em df2 e, caso esteja, "puxar" o nome.

Pesquisei o iterrows e itertuples do pandas, mas não consegui imaginar o código para que, em cada iteração em df1, eu itere novamente em df2.

Também pensei em iterar como se fosse uma matriz com um laço for dentro do outro, mas não deu certo.

Poderiam me ajudar?

8 respostas

Olá Marcelo,

Acho que a função merge do pandas pode te ajudar nesse cenário.

Exemplo:

import pandas as pd

data1 = {'id': ['1', '2', '3', '4', '5'],
        'nome': ['A', 'C', 'E', 'G', 'I']}
df1 = pd.DataFrame(data1, columns = ['id', 'nome'])

data2 = {'id': ['1', '2', '3', '5']}
df2 = pd.DataFrame(data2, columns = ['id'])

df_merge = pd.merge(df1, df2, on='id')

DataFrames:

df1df2Resultado

Se não for esse o seu cenário, compartilha um exemplo dos seus dados iniciais e do resultado esperado.

Qualquer dúvida é só falar!

Tentei usar o merge. Tem alguns problemas:

1) ao usar o merge o dataframe gerado não tem os registros que não tiverem a coluna nome no df2. O que eu gostaria é que fosse criada a coluna nome no df2 e, quando eu tiver o nome em df1, que seja copiado para df2. Mas caso não tenha o nome em df1 fique em branco, mas mantenha o index.

2) no caso do meu dataframe, trata-se de registros de matrícula. Assim, eu tenho no meu df1 mais de um registro para o mesmo id (pq ele se matriculou em disciplinas diferentes). Quando eu uso o merge, caso o id tenha mais de uma matrícula em df1, aparece dobrado no novo dataframe.

Eu sei que dá pra limpar isso tudo, mas eu me pergunto se não tem como fazer isso usando um laço que faça uma busca, tipo

if df2.id[i] == df1.id[j]:
    df2.nome[i] = df1.nome[j]

Mas eu não consegui escrever um código assim que funcionasse.

Agradeço muito a ajuda,

Marcelo

Certo, tenta então com a opção how='right' do merge, diz se isso resolve.

df_merge_col = pd.merge(df1, df2, on='id', how='right')

Utilizando o how você pode fazer algo parecido com um join do sql, e nesse caso você quer fazer um join right, manter todos os dados da tabela direita e trazer os dados da tabela esquerda que estão incluídos na comparação do campo id

Lucas, ainda está estranho, pq o novo df tem mais linhas que o anterior

Estou relutando a postar o código aqui pq tem informações pessoais.

eu estou aplicando o merge da seguinte maneira:

dados16b = pd.merge(matriculas_ftdc, dados16, on='matricula', how='right')

mas ao verificar o tamanho de dados16b:

print(dados16.shape[0], dados16b.shape[0])

o output é 185 217

o df matriculas_ftdc tem as colunas:

['curso', 'tipo', 'matricula', 'nome', 'situacao', 'disciplina', 'ano',
       'semestre']

o df dados16, depois do tratamento que fiz, tem as colunas:

'disciplina', 'matricula', 'situacao', 'ano', 'semestre'

quero acrescentar as colunas 'nome' e 'curso' em dados16

PS: está claro pra mim que preciso fazer os cursos de SQL rsrs

A diferença entre o número de linhas deve ser pelas matrículas repetidas como você falou, então testa se fazendo algo como abaixo ajuda (removendo os valores duplicados antes do merge):

df_merge_col = pd.merge(df1.drop_duplicates(subset='id'), df2, on='id', how='right')

Sobre o SQL nem precisa se preocupar, eu só utilizei como um exemplo para tentar ajudar.

Como alternativa caso o resultado do merge ainda não seja o ideal, você pode utilizar um dos códigos abaixo para fazer a busca e adição do nome:

Utilizando um for:

for index, row in dados16.iterrows():
    r = matriculas_ftdc.loc[matriculas_ftdc['id'] == row['id']]

    if not r.empty:
        dados16.at[index, 'nome'] = r.iloc[0]['nome']
    else:
        dados16.at[index, 'nome'] = 'Nome não encontrado'

Utilizando o apply:

def add_name(row, second_df):
    r = second_df.loc[second_df['id'] == row['id']]

    if not r.empty:
        row['nome'] = r.iloc[0]['nome']
    else:
        row['nome'] = 'Nome não encontrado'

    return row


dados16.apply(add_name, args=(matriculas_ftdc,), axis=1)

Obs.: Antes de tudo confere se o método do tópico anterior resolve.

Obs. 2: Verifica se utilizei os DataFrames corretos nos locais corretos acima, estou buscando o nome em matriculas_ftdc e colocando o nome em dados16.

Obs. 3: Esse método com o iterrows() pode ser lento caso tenha muitas linhas.

solução!

Oi Lucas!

Eu acabei encontrando uma solução aqui. Vou deixar registrado para que possa ajudar outras pessoas com a mesma dúvida.

O que eu fiz:

usei o matriculas_ftdc.drop_duplicates(['matricula']) para ficar apenas com registros únicos no df que eu queria combinar.

apliquei o set_index(['matricula']) em ambos dataframes

usei o join() para criar um 3o dataframe. Deu certinho. O número de linhas deste dataframe é igual ao original.

Agradeço muito sua ajuda!

Sem problemas, obrigado por compartilhar a solução!