3
votes

Calculer la différence de temps, si la différence est supérieure à une heure, marquer comme `` manquant '', tracer l'écart dans le graphique linéaire dans cette zone

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:

Exemple

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.


4 commentaires

Tout d'abord, vous avez un bug. Votre première valeur dans timediff est 43497 jours 01:52:16 . Deuxièmement, que doit représenter l'axe y dans votre graphique? Evidemment, le x représente le point dans le temps


hmm, 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)


3 Réponses :


2
votes

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='*')

 resampled plot


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

 chunked plot

Vous pouvez également injecter des" trous "dans vos données.

df = df.set_index('datetime')
df

 tracé avec trous


4 commentaires

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



0
votes

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()

 Output


0 commentaires

0
votes

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")))


0 commentaires