Je lis des données météorologiques automatisées sur le Web. Les observations ont lieu toutes les 5 minutes et sont compilées dans des fichiers mensuels pour chaque station météorologique. Une fois que j'ai fini d'analyser un fichier, le DataFrame ressemble à ceci:
Sta Precip1hr Precip5min Temp DewPnt WindSpd WindDir AtmPress
Date
2001-01-01 00:00:00 KPDX 0 0 4 3 0 0 30.31
2001-01-01 00:05:00 KPDX 0 0 4 3 0 0 30.30
2001-01-01 00:10:00 KPDX 0 0 4 3 4 80 30.30
2001-01-01 00:15:00 KPDX 0 0 3 2 5 90 30.30
2001-01-01 00:20:00 KPDX 0 0 3 2 10 110 30.28
Le problème que j'ai, c'est que parfois un scientifique revient en arrière et corrige des observations - non pas en modifiant les lignes erronées, mais en ajoutant une ligne en double à la fin d'un fichier. Un exemple simple d'un tel cas est illustré ci-dessous:
import pandas
import datetime
startdate = datetime.datetime(2001, 1, 1, 0, 0)
enddate = datetime.datetime(2001, 1, 1, 5, 0)
index = pandas.DatetimeIndex(start=startdate, end=enddate, freq='H')
data1 = {'A' : range(6), 'B' : range(6)}
data2 = {'A' : [20, -30, 40], 'B' : [-50, 60, -70]}
df1 = pandas.DataFrame(data=data1, index=index)
df2 = pandas.DataFrame(data=data2, index=index[:3])
df3 = df2.append(df1)
df3
A B
2001-01-01 00:00:00 20 -50
2001-01-01 01:00:00 -30 60
2001-01-01 02:00:00 40 -70
2001-01-01 03:00:00 3 3
2001-01-01 04:00:00 4 4
2001-01-01 05:00:00 5 5
2001-01-01 00:00:00 0 0
2001-01-01 01:00:00 1 1
2001-01-01 02:00:00 2 2
Et donc j'ai besoin df3
de devenir de façon équilibrée:
A B
2001-01-01 00:00:00 0 0
2001-01-01 01:00:00 1 1
2001-01-01 02:00:00 2 2
2001-01-01 03:00:00 3 3
2001-01-01 04:00:00 4 4
2001-01-01 05:00:00 5 5
Je pensais que l'ajout d'une colonne de numéros de ligne ( df3['rownum'] = range(df3.shape[0])
) m'aiderait à sélectionner la ligne la plus basse pour n'importe quelle valeur de DatetimeIndex
, mais je suis coincé à trouver les instructions group_by
ou pivot
(ou ???) pour que cela fonctionne.
Réponses:
Je suggère d'utiliser la méthode dupliquée sur l'index Pandas lui-même:
Bien que toutes les autres méthodes fonctionnent, la réponse actuellement acceptée est de loin la moins performante pour l'exemple fourni. De plus, bien que la méthode groupby ne soit que légèrement moins performante, je trouve que la méthode dupliquée est plus lisible.
En utilisant les exemples de données fournis:
Notez que vous pouvez conserver le dernier élément en modifiant l'argument keep.
Il convient également de noter que cette méthode fonctionne également avec
MultiIndex
(en utilisant df1 comme spécifié dans l'exemple de Paul ):la source
loc
pourrait ne pas être nécessaire. Faites simplementdf3 = df3[~df3.index.duplicated(keep='first')]
, ce qui supprimera toutes les lignes avec un index en double, sauf la première occurrence.Une solution simple consiste à utiliser
drop_duplicates
Pour moi, cela a fonctionné rapidement sur de grands ensembles de données.
Cela nécessite que «rownum» soit la colonne contenant les doublons. Dans l'exemple modifié, 'rownum' n'a pas de doublons, donc rien n'est éliminé. Ce que nous voulons vraiment, c'est que les «cols» soient mis à l'index. Je n'ai pas trouvé de moyen de dire à drop_duplicates de ne considérer que l'index.
Voici une solution qui ajoute l'index en tant que colonne de trame de données, supprime les doublons à ce sujet, puis supprime la nouvelle colonne:
Et si vous voulez que les choses
sort
soient remises dans le bon ordre, appelez simplement la trame de données.la source
df.reset_index().drop_duplicates(cols='index',take_last=True).set_index('index')
reset_index()
ajoute les colonnes level_0, level_1, etc. Et si votre index a un nom, ce nom sera utilisé à la place de l'étiquette "index". Cela en fait un peu plus qu'une ligne pour le faire correctement pour n'importe quel DataFrame.index_label = getattr(df.index, 'names', getattr(df.index, 'name', 'index'))
puiscols=index_label
alorsset_index(index_labels)
et même ce n'est pas infaillible (ne fonctionnera pas pour les multi-index sans nom).idx = df.index.name or 'index'
, on pourrait aussi fairedf2 = df.reset_index(); df2.drop_duplicates(idx, inplace=True); df2.set_index(idx, inplace=True)
pour éviter les copies intermédiaires (à cause duinplace=True
)Oh mon. C'est en fait si simple!
Édition de suivi 2013-10-29 Dans le cas où j'ai un assez complexe
MultiIndex
, je pense que je préfère l'groupby
approche. Voici un exemple simple pour la postérité:et voici la partie importante
la source
level=[0,1]
fonctionnera s'il y a 2 niveauxdf1.groupby(level=[0,1]).last()
. Cela devrait faire partie des Pandas en complément dedrop_duplicates
df.index.names
n'est qu'un moyen simple de regrouper tous les niveaux de l'index.xarray
pour traiter les index DateTime en double ainsi que la fabricationds.resample
et lesds.groupby
opérations échouentxarray
aussi longtemps que vous changez legrouped = df3.groupby(level=0)
engrouped = df3.groupby(dim='time')
ou quelle que soit la dimension qui contient des doublonsMalheureusement, je ne pense pas que Pandas permette de supprimer les doublons des indices. Je suggérerais ce qui suit:
la source
Si quelqu'un comme moi aime la manipulation de données chaînables en utilisant la notation de point pandas (comme la tuyauterie), alors ce qui suit peut être utile:
Cela permet de chaîner des instructions comme celle-ci:
la source
TypeError: 'Series' objects are mutable, thus they cannot be hashed
.. Est-ce que cela a fonctionné pour vous?Supprimer les doublons (continuer en premier)
Supprimer les doublons (Garder le dernier)
Tests: boucles 10k utilisant les données OP
la source