J'ai une base de données pandas en python, qui prend des données et trace un graphique linéaire. Chaque point de données implique un temps. Si tout fonctionne bien avec le fichier de données, idéalement, chaque horodatage est d'environ 30 minutes différent l'un de l'autre. Dans certains cas, aucune donnée n'est transmise pendant plus d'une heure. Pendant ces périodes, je souhaite marquer cette période comme `` manquante '' et tracer un graphique linéaire discontinu, montrant de manière flagrante les données manquantes.
J'ai du mal à comprendre comment faire cela et même à rechercher un solution car le problème est assez spécifique. Les données sont «en direct» où elles sont constamment mises à jour, donc je ne peux pas simplement identifier une certaine zone et les modifier comme solution de contournement.
Quelque chose qui ressemble à ceci:
Code utilisé pour créer la colonne datetime:
datetime l1 l2 l3 2019-02-03 01:52:16 0.1 0.2 0.4 2019-02-03 02:29:26 0.1 0.3 0.6 2019-02-03 02:48:03 0.1 0.3 0.6 2019-02-03 04:48:52 0.3 0.8 1.4 2019-02-03 05:25:59 0.4 1.1 1.7 2019-02-03 05:44:34 0.4 1.3 2.2
J'ai trouvé comment calculer le décalage horaire, ce qui impliquait de créer une nouvelle colonne. Voici ce code au cas où:
df['timediff'] = (df['datetime']-df['datetime'].shift().fillna(pd.to_datetime("00:00:00", format="%H:%M:%S")))
Aperçu de base de dataframe:
#convert first time columns into one datetime column df['datetime'] = pd.to_datetime(df[['year', 'month', 'day', 'hour', 'minute', 'second']])
Je ne sais pas comment pour créer un tracé "en direct" discontinu impliquant le décalage horaire.
Merci d'avance.
3 Réponses :
Ce n'est pas exactement ce que vous voulez, mais une solution rapide et élégante consiste à rééchantillonner vos données.
# find samples which occurred more than an hour after the previous # sample holes = df.loc[td > timedelta(hours=1)] # "holes" occur just before these samples holes.index -= timedelta(microseconds=1) # append holes to the data, set values to NaN df = df.append(holes) df.loc[holes.index] = np.nan # plot series df['l1'].plot(marker='*')
from datetime import timedelta # get difference between consecutive timestamps dt = df.index.to_series() td = dt - dt.shift() # generate a new group index every time the time difference exceeds # an hour gp = np.cumsum(td > timedelta(hours=1)) # get current axes, plot all groups on the same axes ax = plt.gca() for _, chunk in df.groupby(gp): chunk['l1'].plot(marker='*', ax=ax)
df.resample('30T').mean()['l1'].plot(marker='*')
Si vous avez absolument besoin de tracer chaque échantillon exactement, vous pouvez diviser vos données là où la différence entre les horodatages consécutifs dépasse un certain seuil, et tracer chaque morceau séparément.
l1 l2 l3 datetime 2019-02-03 01:52:16 0.1 0.2 0.4 2019-02-03 02:29:26 0.1 0.3 0.6 2019-02-03 02:48:03 0.1 0.3 0.6 2019-02-03 04:48:52 0.3 0.8 1.4 2019-02-03 05:25:59 0.4 1.1 1.7 2019-02-03 05:44:34 0.4 1.3 2.2
Vous pouvez également injecter des" trous "dans vos données.
df = df.set_index('datetime') df
Je pense que les trous sont ce que je veux faire, mais j'obtiens une erreur de clé avec df.set_index ('datetime') (KeyError: 'datetime'). J'utilise set_index avec datetime pour rééchantillonner plus tôt dans le code pour obtenir des valeurs moyennes et maximales, ce qui pourrait être le problème, mais je ne suis pas sûr.
@ user279955, si votre bloc de données est déjà indexé par l'horodatage, vous pouvez ignorer cette étape.
l'indexation semble correcte lorsque j'imprime chaque étape, mais j'obtiens l'erreur clé lorsque je le fais. Quand je ne reçois pas cette erreur: TypeError: '>' non pris en charge entre les instances de 'float' et 'datetime.timedelta'
c'est-à-dire que je peux voir le nouvel index mis en place où chaque décalage horaire est précis. l'erreur apparaît dans l'instance suivante de df ['_'] comme lorsque je vais tracer
Edit: @Igor Raush a donné une meilleure réponse, mais je la laisse quand même car la visualisation est un peu différente.
Voyez si cela vous aide:
import pandas as pd import numpy as np import matplotlib.pyplot as plt # Track the time delta in seconds # I used total_seconds() and not seconds as seconds are limited to the amount of secs in one day df['timediff'] = (df['datetime'] - df['datetime'].shift(1)).dt.total_seconds().cumsum().fillna(0) # Create a dataframe of all the possible seconds in the time range all_times_df = pd.DataFrame(np.arange(df['timediff'].min(), df['timediff'].max()), columns=['timediff']).set_index('timediff') # Join the dataframes and fill nulls with 0s, so the values change only where data has been received live_df = all_times_df.join(df.set_index('timediff')).ffill() # Plot only your desired columns live_df[['l1', 'l3']].plot() plt.show()
Résolu en utilisant ma nouvelle colonne timediff et la fonction df.loc.
missing_l1 = df['l1'].loc[df['timediff'] > timedelta(hours=1)] = np.nan missing_l2 = df['l2'].loc[df['timediff'] > timedelta(hours=1)] = np.nan
Avec cela, j'ai pu recueillir le décalage horaire pour chaque ligne.
Ensuite, en utilisant df.loc, j'ai pu localiser des valeurs dans les colonnes l1 et l2 où timediff était supérieur à une heure, et faire alors un nan. Le résultat est une ligne manquante dans le graphique à ce moment-là, comme je le voulais.
df['timediff'] = (df['datetime']-df['datetime'].shift().fillna(pd.to_datetime("00:00:00", format="%H:%M:%S")))
Tout d'abord, vous avez un bug. Votre première valeur dans
timediff
est43497 jours 01:52:16
. Deuxièmement, que doit représenter l'axey
dans votre graphique? Evidemment, lex
représente le point dans le tempshmm, je ne l'ai pas remarqué au début. pas sûr que cela compte car le reste est correct et je ne me concentre que sur les heures? l'axe y est juste l1 et l3 qui sont des pourcentages
Je voulais dire que je ne sais pas ce que vous aimeriez que le graphique cible montre. Que souhaitez-vous que l'axe
y
du graphique souhaité représente? Que mesure-t-il? Est-ce la somme de l1 + l2 + l3?Je trace deux lignes, l1 et l3, les nombres sont les y-cordons pour chaque x-cord (temps)