2017-10-08 4 views
0

J'ai reçu neuf données différentes que je veux joindre (ou fusionner ou mettre à jour) dans une seule trame de données. Chacune de ces bases de données d'origine est composée de deux colonnes seulement, une en secondes et une valeur pour cette observation. Les données ressemble à ceci:Pandas: joindre des données et fusionner des valeurs de colonnes identiques

Filter_type   Time 
0   0.0 6333.137168 


    Filter_type   Time 
0   0.0 6347.422576 


    Filter_type   Time 
0   0.0 7002.406185 


    Filter_type   Time 
0   0.0 7015.845717 


    Sign_pos_X   Time 
0  11.5 6333.137168 
1  25.0 6347.422576 
2  25.5 7002.406185 
3  38.0 7015.845717 


    Sign_pos_Y   Time 
0  -3.0 6333.137168 
1   8.0 6347.422576 
2  -7.5 7002.406185 
3  -0.5 7015.845717 


    Sign_pos_Z   Time 
0   1.0 6333.137168 
1   1.0 6347.422576 
2   1.0 7002.406185 
3   7.5 7015.845717 


    Supplementary_sign_type   Time 
0      0.0 6333.137168 
1      0.0 6347.422576 
2      0.0 7002.406185 
3      0.0 7015.845717 


      Time vision_only_sign_type 
0 6333.137168     7.0 
1 6347.422576     9.0 
2 7002.406185     9.0 
3 7015.845717     35.0 

Depuis que je veux rejoindre tous en un seul dataframe, j'ai essayé les éléments suivants:

df2 = None 

for cell in df['Frames']: 
    if not isinstance(cell, list): 
     continue 

    df_ = pd.DataFrame(cell) 
    if df2 is None: 
     # first iteration 
     df2 = df_ 
     continue 

    df2 = df2.merge(df_, on='Offset', how='outer') 
    #df2 = df2.join(df_) 
    #df2.update(df_, join='outer') 

df2 

Le problème est que les quatre premiers dataframes ont la même nom de la colonne de valeur tandis que les autres ne le font pas. Par conséquent, le résultat a trois colonnes avec le préfixe « FILTER_TYPE »:

+----+-----------------+----------+-----------------+-----------------+-----------------+--------------+--------------+--------------+---------------------------+-------------------------+ 
| | Filter_type_x | Offset | Filter_type_y | Filter_type_x | Filter_type_y | Sign_pos_X | Sign_pos_Y | Sign_pos_Z | Supplementary_sign_type | vision_only_sign_type | 
|----+-----------------+----------+-----------------+-----------------+-----------------+--------------+--------------+--------------+---------------------------+-------------------------| 
| 0 |    0 | 6333.14 |    nan |    nan |    nan |   11.5 |   -3 |   1 |       0 |      7 | 
| 1 |    nan | 6347.42 |    0 |    nan |    nan |   25 |   8 |   1 |       0 |      9 | 
| 2 |    nan | 7002.41 |    nan |    0 |    nan |   25.5 |   -7.5 |   1 |       0 |      9 | 
| 3 |    nan | 7015.85 |    nan |    nan |    0 |   38 |   -0.5 |   7.5 |       0 |      35 | 
+----+-----------------+----------+-----------------+-----------------+-----------------+--------------+--------------+--------------+---------------------------+-------------------------+ 

Ma question est: Comment puis-je forcer la fusion/join pour joindre toutes les colonnes « FILTER_TYPE » en un seul. Vous pouvez voir que chaque ligne n'a qu'une seule valeur dans toutes ces colonnes alors que les autres sont NaN. Résultat devrait ressembler à ceci (ayant une seule colonne fusionnée « FILTER_TYPE »):

+----+----------+--------------+--------------+--------------+---------------------------+-------------------------+---------------+ 
| | Offset | Sign_pos_X | Sign_pos_Y | Sign_pos_Z | Supplementary_sign_type | vision_only_sign_type | Filter_type | 
|----+----------+--------------+--------------+--------------+---------------------------+-------------------------+---------------| 
| 0 | 6333.14 |   11.5 |   -3 |   1 |       0 |      7 |    0 | 
| 1 | 6347.42 |   25 |   8 |   1 |       0 |      9 |    0 | 
| 2 | 7002.41 |   25.5 |   -7.5 |   1 |       0 |      9 |    0 | 
| 3 | 7015.85 |   38 |   -0.5 |   7.5 |       0 |      35 |    0 | 
+----+----------+--------------+--------------+--------------+---------------------------+-------------------------+---------------+ 

Répondre

1

Appel pd.merge dans une boucle conduit à quadratic copying et un ralentissement des performances lorsque la longueur ou le nombre même de DataFrames est grande. Donc, évitez ceci si possible.

Ici, il semble que nous voulons concaténer les DataFrames verticalement quand ils ont des colonnes Time et Filter_type, et nous souhaitons concaténer horizontalement lorsque les DataFrames manquent une colonne Filter_type:

frames = [df.set_index('Time') for df in frames] 
filter_type_frames = pd.concat(frames[:4], axis=0) 
result = pd.concat([filter_type_frames] + frames[4:], axis=1) 
result = result.reset_index('Time') 
print(result) 

Appel pd.concat avec axis=0 Concatène verticalement, avec axis=1 horizontalement. Depuis pd.concat accepte une liste de DataFrames et peut les concaténer tous en même temps sans créer itérativement DataFrames intermédiaires, pd.concat évite le problème de copie quadratique.

Étant donné que pd.concat aligne les index, en définissant l'index sur Time, les données sont alignées correctement en fonction de Time.

Voir ci-dessous pour un exemple exécutable.


Il y a une autre façon de résoudre le problème, et d'une manière il est plus joli, mais il appelle pd.merge dans une boucle et peut donc souffrir d'une mauvaise performance pour la raison expliquée ci-dessus.

L'idée, cependant, est la suivante: Par défaut, pd.merge(left, right) fusionne sur toutes les étiquettes de colonne que left et right partagent en commun. Donc, si vous omettez on='Offset' (ou `sur « Time »=?) Et utilisez

df2 = df2.merge(df_, how='outer') 

puis la fusion se joindra à la fois Offset (ou Time) et Filter_type si les deux existent.


Vous pouvez simplifier davantage la boucle en utilisant

import functools 
df2 = functools.reduce(functools.partial(pd.merge, how='outer'), df['Frames']) 

La boucle est cachée à l'intérieur functools.reduce, mais en substance, pd.merge est toujours appelé dans une boucle. Donc, bien que ce soit joli, il peut ne pas être performant.


import functools 
import pandas as pd 
frames = [pd.DataFrame({'Filter_type': [0.0], 'Time': [6333.137168]}), 
      pd.DataFrame({'Filter_type': [0.0], 'Time': [6347.422576]}), 
      pd.DataFrame({'Filter_type': [0.0], 'Time': [7002.406185]}), 
      pd.DataFrame({'Filter_type': [0.0], 'Time': [7015.845717]}), 
      pd.DataFrame({'Sign_pos_X': [11.5, 25.0, 25.5, 38.0], 
         'Time': [6333.137168, 6347.422576, 7002.406185, 7015.845717]}), 
      pd.DataFrame({'Sign_pos_Y': [-3.0, 8.0, -7.5, -0.5], 
         'Time': [6333.137168, 6347.422576, 7002.406185, 7015.845717]}), 
      pd.DataFrame({'Sign_pos_Z': [1.0, 1.0, 1.0, 7.5], 
         'Time': [6333.137168, 6347.422576, 7002.406185, 7015.845717]}), 
      pd.DataFrame({'Supplementary_sign_type': [0.0, 0.0, 0.0, 0.0], 
         'Time': [6333.137168, 6347.422576, 7002.406185, 7015.845717]}), 
      pd.DataFrame({'Time': [6333.137168, 6347.422576, 7002.406185, 7015.845717], 
         'vision_only_sign_type': [7.0, 9.0, 9.0, 35.0]})] 

result = functools.reduce(functools.partial(pd.merge, how='outer'), frames) 
print(result) 

frames = [df.set_index('Time') for df in frames] 
A = pd.concat(frames[:4], axis=0) 
result = pd.concat([A] + frames[4:], axis=1) 
result = result.reset_index('Time') 
print(result) 
# same result 

impressions

Filter_type   Time Sign_pos_X Sign_pos_Y Sign_pos_Z \ 
0   0.0 6333.137168  11.5  -3.0   1.0 
1   0.0 6347.422576  25.0   8.0   1.0 
2   0.0 7002.406185  25.5  -7.5   1.0 
3   0.0 7015.845717  38.0  -0.5   7.5 

    Supplementary_sign_type vision_only_sign_type 
0      0.0     7.0 
1      0.0     9.0 
2      0.0     9.0 
3      0.0     35.0 
+0

Très belle solution. En attendant, j'ai aussi trouvé la solution de concaténer les premières images. Mais j'aime vraiment ton appel de réduction. Va vérifier cela aussi! – Matthias