Comment conserver l'index lors de l'utilisation de pandas merge

126

Je voudrais en fusionner deux DataFrameset conserver l'index de la première image en tant qu'index sur l'ensemble de données fusionné. Cependant, lorsque je fais la fusion, le DataFrame résultant a un index entier. Comment puis-je spécifier que je souhaite conserver l'index du bloc de données gauche?

In [4]: a = pd.DataFrame({'col1': {'a': 1, 'b': 2, 'c': 3}, 
                          'to_merge_on': {'a': 1, 'b': 3, 'c': 4}})

In [5]: b = pd.DataFrame({'col2': {0: 1, 1: 2, 2: 3}, 
                          'to_merge_on': {0: 1, 1: 3, 2: 5}})

In [6]: a
Out[6]:
   col1  to_merge_on
a     1            1
b     2            3
c     3            4

In [7]: b
Out[7]:
   col2  to_merge_on
0     1            1
1     2            3
2     3            5

In [8]: a.merge(b, how='left')
Out[8]:
   col1  to_merge_on  col2
0     1            1   1.0
1     2            3   2.0
2     3            4   NaN

In [9]: _.index
Out[9]: Int64Index([0, 1, 2], dtype='int64')

EDIT: Passé à un exemple de code qui peut être facilement reproduit

DanB
la source
2
si vous fusionnez sur une colonne spécifique, il n'est pas clair quels index utiliser (au cas où ils seraient tous les deux différents).
bonobo

Réponses:

161
In [5]: a.reset_index().merge(b, how="left").set_index('index')
Out[5]:
       col1  to_merge_on  col2
index
a         1            1     1
b         2            3     2
c         3            4   NaN

Remarque: pour certaines opérations de fusion à gauche, vous pouvez vous retrouver avec plus de lignes s'il y a plusieurs correspondances entre aet bet vous devrez dédupliquer ( documentation pour la déduplication ). C'est pourquoi pandas ne garde pas l'index pour vous.

Wouter Overmeire
la source
4
Très intelligent. a.merge (b, how = "left"). set_index (a.index) fonctionne également, mais cela semble moins robuste (puisque la première partie perd les valeurs d'index à a avant de les réinitialiser.)
DanB
11
Pour ce cas particulier, ceux-ci sont équivalents. Mais pour de nombreuses opérations de fusion, l'image résultante n'a pas le même nombre de lignes que l' aimage d' origine . reset_index déplace l'index vers une colonne normale et set_index de cette colonne après la fusion prend également soin lorsque les lignes d'un sont dupliquées / supprimées en raison de l'opération de fusion.
Wouter Overmeire
1
@Wouter J'aimerais savoir pourquoi une fusion à gauche sera réindexée par défaut. Où puis-je en savoir plus?
Matthew
7
Agréable! Pour éviter de spécifier explicitement le nom d'index que j'utilise a.reset_index().merge(b, how="left").set_index(a.index.names).
Truls
3
Les pandas pensaient que l'API frappait à nouveau.
Henry Henrinson
7

Vous pouvez faire une copie de l'index sur la trame de données gauche et procéder à la fusion.

a['copy_index'] = a.index
a.merge(b, how='left')

J'ai trouvé cette méthode simple très utile tout en travaillant avec une grande trame de données et en utilisant pd.merge_asof()(ou dd.merge_asof()).

Cette approche serait meilleure lorsque la réinitialisation de l'index est coûteuse (grande trame de données).

Matthew Son
la source
1
C'est la meilleure réponse. Il existe de nombreuses raisons pour lesquelles vous voudriez conserver vos anciens index lors d'une fusion (et la réponse acceptée ne conserve pas les index, elle les réinitialise simplement). Cela aide lorsque vous essayez de fusionner plus de 2 dataframes, et ainsi de suite ...
Marses
2
Solution supérieure car elle préserve le nom d'index (original)
Martien Lubberink
voté mais méfiez-vous simplement d'une mise en garde, lorsque vous utilisez le multi-index, vos index seront stockés sous forme de tuple dans une seule colonne appelée [copy_index]
geekidharsh
6

Il existe une solution non-pd.merge. Utilisation mapetset_index

In [1744]: a.assign(col2=a['to_merge_on'].map(b.set_index('to_merge_on')['col2']))
Out[1744]:
   col1  to_merge_on  col2
a     1            1   1.0
b     2            3   2.0
c     3            4   NaN

Et n'introduit pas de indexnom factice pour l'index.

Zéro
la source
1
Cela semble supérieur à la réponse acceptée car cela fonctionnera probablement mieux avec les cas extrêmes comme les index multiples. Quelqu'un peut-il commenter cela?
BallpointBen
1
question, que se passe-t-il si vous devez attribuer plusieurs colonnes, cette approche fonctionnerait-elle ou est-elle limitée à un seul champ?
Yuca
@Yuca: Cela ne fonctionnera peut-être pas avec plusieurs colonnes, car lorsque vous sous-ensemble plusieurs colonnes, vous vous retrouvez avec un pd.Dataframeet pas un pd.Series. La .map()méthode n'est définie que pour le pd.Series. Cela signifie que: a[['to_merge_on_1', 'to_merge_on_2']].map(...)ne fonctionnera pas.
Dataman le
4
df1 = df1.merge(
        df2, how="inner", left_index=True, right_index=True
    )

Cela permet de conserver l'index de df1

Supratik Majumdar
la source
Il semble fonctionner, mais quand je l' utilise avec on=list_of_cols], elle contredit la documentation: If joining columns on columns, the DataFrame indexes *will be ignored*. Est-ce que l'un de l'utilisation d'indices par rapport aux colonnes a la priorité?
Itamar Katz le
0

Je pense avoir trouvé une solution différente. Je rejoignais la table de gauche sur la valeur d'index et la table de droite sur une valeur de colonne basée sur l'index de la table de gauche. Ce que j'ai fait était une fusion normale:

First10ReviewsJoined = pd.merge(First10Reviews, df, left_index=True, right_on='Line Number')

Ensuite, j'ai récupéré les nouveaux numéros d'index de la table fusionnée et les ai mis dans une nouvelle colonne nommée Numéro de ligne de sentiment:

First10ReviewsJoined['Sentiment Line Number']= First10ReviewsJoined.index.tolist()

Ensuite, je redéfinis manuellement l'index sur l'index de table de gauche d'origine en fonction de la colonne préexistante appelée Numéro de ligne (la valeur de la colonne à laquelle j'ai rejoint l'index de la table de gauche):

First10ReviewsJoined.set_index('Line Number', inplace=True)

Ensuite, supprimez le nom d'index du numéro de ligne afin qu'il reste vide:

First10ReviewsJoined.index.name = None

Peut-être un peu un hack mais semble bien fonctionner et relativement simple. De plus, supposez que cela réduit le risque de doublons / de falsification de vos données. Espérons que tout a du sens.

le développeur
la source
0

une autre option simple est de renommer l'index comme avant:

a.merge(b, how="left").set_axis(a.index)

merge préserve l'ordre au dataframe 'a', mais réinitialise simplement l'index pour qu'il soit sauvegardé pour utiliser set_axis

lisrael1
la source