Tenho dois quadros de dados df1 e df2, onde df2 é um subconjunto de df1. Como obtenho um novo quadro de dados (df3), que é a diferença entre os dois quadros de dados?
Em outras palavras, um quadro de dados que possui todas as linhas / colunas em df1 que não estão em df2?
Respostas:
Usando
drop_duplicates
pd.concat([df1,df2]).drop_duplicates(keep=False)
Update :
df1=pd.DataFrame({'A':[1,2,3,3],'B':[2,3,4,4]}) df2=pd.DataFrame({'A':[1],'B':[2]})
A saída será como abaixo, o que está errado
pd.concat([df1, df2]).drop_duplicates(keep=False) Out[655]: A B 1 2 3
Out[656]: A B 1 2 3 2 3 4 3 3 4
Método 1: usando
isin
comtuple
df1[~df1.apply(tuple,1).isin(df2.apply(tuple,1))] Out[657]: A B 1 2 3 2 3 4 3 3 4
Método 2:
merge
comindicator
df1.merge(df2,indicator = True, how='left').loc[lambda x : x['_merge']!='both'] Out[421]: A B _merge 1 2 3 left_only 2 3 4 left_only 3 3 4 left_only
fonte
pd.concat([df1,df2]).drop_duplicates(subset = ['col1','col2'], keep=False)
float
(because12.00000000001 != 12
). Uma prática melhor é encontrar a interseção do conjunto dos IDs em dois quadros de dados e obter a diferença com base nisso.indicator=True
) é uma ferramenta muito versátil e útil, eu adoraria vê-lo no início desta resposta, mas com junção 'externa' e não 'esquerda' para cobrir todas as 3 situações.Para linhas, tente isto, onde
Name
é a coluna de índice comum (pode ser uma lista para várias colunas comuns ou especifiqueleft_on
eright_on
):m = df1.merge(df2, on='Name', how='outer', suffixes=['', '_'], indicator=True)
A
indicator=True
configuração é útil porque adiciona uma coluna chamada_merge
, com todas as mudanças entredf1
edf2
, categorizada em 3 tipos possíveis: "left_only", "right_only" ou "both".Para colunas, tente isto:
set(df1.columns).symmetric_difference(df2.columns)
fonte
merge
comindicator=True
é a solução clássica para comparar dataframes por campos dados.Resposta aceita O Método 1 não funcionará para quadros de dados com NaNs dentro, como
pd.np.nan != pd.np.nan
. Não tenho certeza se esta é a melhor maneira, mas pode ser evitadadf1[~df1.astype(str).apply(tuple, 1).isin(df2.astype(str).apply(tuple, 1))]
fonte
edit2, descobri uma nova solução sem a necessidade de definir o índice
newdf=pd.concat([df1,df2]).drop_duplicates(keep=False)
Ok, descobri que a resposta do maior voto já contém o que descobri. Sim, só podemos usar este código com a condição de que não haja duplicatas em cada dois dfs.
Eu tenho um método complicado. Primeiro, definimos 'Nome' como o índice de dois dataframes dados pela pergunta. Como temos o mesmo 'Nome' em dois dfs, podemos simplesmente remover o índice do df 'menor' do df 'maior'. Aqui está o código.
df1.set_index('Name',inplace=True) df2.set_index('Name',inplace=True) newdf=df1.drop(df2.index)
fonte
import pandas as pd # given df1 = pd.DataFrame({'Name':['John','Mike','Smith','Wale','Marry','Tom','Menda','Bolt','Yuswa',], 'Age':[23,45,12,34,27,44,28,39,40]}) df2 = pd.DataFrame({'Name':['John','Smith','Wale','Tom','Menda','Yuswa',], 'Age':[23,12,34,44,28,40]}) # find elements in df1 that are not in df2 df_1notin2 = df1[~(df1['Name'].isin(df2['Name']) & df1['Age'].isin(df2['Age']))].reset_index(drop=True) # output: print('df1\n', df1) print('df2\n', df2) print('df_1notin2\n', df_1notin2) # df1 # Age Name # 0 23 John # 1 45 Mike # 2 12 Smith # 3 34 Wale # 4 27 Marry # 5 44 Tom # 6 28 Menda # 7 39 Bolt # 8 40 Yuswa # df2 # Age Name # 0 23 John # 1 12 Smith # 2 34 Wale # 3 44 Tom # 4 28 Menda # 5 40 Yuswa # df_1notin2 # Age Name # 0 45 Mike # 1 27 Marry # 2 39 Bolt
fonte
Talvez uma linha mais simples, com nomes de coluna idênticos ou diferentes. Funcionou mesmo quando df2 ['Name2'] continha valores duplicados.
newDf = df1.set_index('Name1') .drop(df2['Name2'], errors='ignore') .reset_index(drop=False)
fonte
Como mencionado aqui que
df1[~df1.apply(tuple,1).isin(df2.apply(tuple,1))]
é a solução correta, mas produzirá uma saída errada se
df1=pd.DataFrame({'A':[1],'B':[2]}) df2=pd.DataFrame({'A':[1,2,3,3],'B':[2,3,4,4]})
Nesse caso, a solução acima fornecerá Empty DataFrame , em vez disso, você deve usar o
concat
método após remover duplicatas de cada datframe.Usar
concate with drop_duplicates
df1=df1.drop_duplicates(keep="first") df2=df2.drop_duplicates(keep="first") pd.concat([df1,df2]).drop_duplicates(keep=False)
fonte
df1[~df1.apply(tuple,1).isin(df2.apply(tuple,1))]
é a resposta correta, mesmo neste caso. Se você deseja obter valores que estão em df1 ou df2, mas não em ambos, sua abordagem sugerida está correta (com a ressalva de remover duplicatas dos dataframes originais).Uma ligeira variação da solução do nice @ liangli que não requer a alteração do índice de dataframes existentes:
newdf = df1.drop(df1.join(df2.set_index('Name').index))
fonte
Encontrando diferença por índice. Assumindo que df1 é um subconjunto de df2 e os índices são transportados durante o subconjunto
df1.loc[set(df1.index).symmetric_difference(set(df2.index))].dropna() # Example df1 = pd.DataFrame({"gender":np.random.choice(['m','f'],size=5), "subject":np.random.choice(["bio","phy","chem"],size=5)}, index = [1,2,3,4,5]) df2 = df1.loc[[1,3,5]] df1 gender subject 1 f bio 2 m chem 3 f phy 4 m bio 5 f bio df2 gender subject 1 f bio 3 f phy 5 f bio df3 = df1.loc[set(df1.index).symmetric_difference(set(df2.index))].dropna() df3 gender subject 2 m chem 4 m bio
fonte
Além da resposta aceita, gostaria de propor mais uma solução mais ampla que pode encontrar uma diferença de conjunto 2D de dois dataframes com qualquer
index
/columns
(eles podem não coincidir para ambos os dados). Além disso, o método permite configurar tolerância parafloat
elementos para comparação de dataframe (ele usanp.isclose
)import numpy as np import pandas as pd def get_dataframe_setdiff2d(df_new: pd.DataFrame, df_old: pd.DataFrame, rtol=1e-03, atol=1e-05) -> pd.DataFrame: """Returns set difference of two pandas DataFrames""" union_index = np.union1d(df_new.index, df_old.index) union_columns = np.union1d(df_new.columns, df_old.columns) new = df_new.reindex(index=union_index, columns=union_columns) old = df_old.reindex(index=union_index, columns=union_columns) mask_diff = ~np.isclose(new, old, rtol, atol) df_bool = pd.DataFrame(mask_diff, union_index, union_columns) df_diff = pd.concat([new[df_bool].stack(), old[df_bool].stack()], axis=1) df_diff.columns = ["New", "Old"] return df_diff
Exemplo:
In [1] df1 = pd.DataFrame({'A':[2,1,2],'C':[2,1,2]}) df2 = pd.DataFrame({'A':[1,1],'B':[1,1]}) print("df1:\n", df1, "\n") print("df2:\n", df2, "\n") diff = get_dataframe_setdiff2d(df1, df2) print("diff:\n", diff, "\n")
Out [1] df1: A C 0 2 2 1 1 1 2 2 2 df2: A B 0 1 1 1 1 1 diff: New Old 0 A 2.0 1.0 B NaN 1.0 C 2.0 NaN 1 B NaN 1.0 C 1.0 NaN 2 A 2.0 NaN C 2.0 NaN
fonte