2 votes

Conversion des séries Pandas en valeurs personnalisées de Dataframe

start = pd.to_datetime("2017-02-21 22:32:41",infer_datetime_format=True)
end = pd.to_datetime("2017-02-22 01:32:41",infer_datetime_format=True)
rng = pd.date_range(start.floor('h'), end.floor('h'), freq='h')
left = pd.Series(rng, index=rng ).clip_lower(start)
right = pd.Series(rng + 1, index=rng).clip_upper(end)
s = right - left

J'obtiens le résultat suivant

2017-02-21 22:00:00   00:27:19
2017-02-21 23:00:00   01:00:00
2017-02-22 00:00:00   01:00:00
2017-02-22 01:00:00   00:32:41

Je veux convertir le résultat pandas.Series vers dataframe pour obtenir le résultat suivant

id    |hour|day|minute|
+-----+----+---+------+
|10001|  22|Wed|     27|
|10001|  23|Thu|     60|
|10001|  00|Thu|     60|
|10001|  01|Thu|     32|

Existe-t-il une possibilité de conversion directe ou dois-je passer par la boucle ?

5voto

piRSquared Points 159

Option 1
Utilisation np.core.defchararray.split après avoir utilisé strftime
Suivi de assign après avoir utilisé la division du plancher sur le nombre de secondes

pd.DataFrame(
    np.core.defchararray.split(s.index.strftime('%H %a')).tolist(),
    columns=['hour', 'day']
).assign(minute=(s.dt.seconds // 60).values)

  hour  day  minute
0   22  Tue      27
1   23  Tue      60
2   00  Wed      60
3   01  Wed      32

Option 2
Utilisation d'un dictionnaire dans une liste de compréhension.
Notez que j'utilise les f-strings de Python 3.6.
Dans le cas contraire, utiliser '{:02d}'.format(i.hour)

pd.DataFrame([dict(
    hour=f'{i.hour:02d}',
    day=i.strftime('%a'),
    minute=v.seconds // 60
) for i, v in s.items()])

   day  hour  minute
0  Tue    22      27
1  Tue    23      60
2  Wed    00      60
3  Wed    01      32

Option 3
Et puisque la question de la vitesse a été soulevée, j'ai voulu proposer une autre option qui en tienne compte.

a = np.array('Mon Tue Wed Thu Fri Sat Sun'.split())

pd.DataFrame(dict(
    hour=s.index.hour.astype(str).str.zfill(2),
    day=a[s.index.weekday],
    minute=s.values.astype('timedelta64[m]').astype(int)
))

   day  hour  minute
0  Tue    22      27
1  Tue    23      60
2  Wed    00      60
3  Wed    01      32

Test de temps complet

Remarque : J'ai modifié les fonctions pour m'assurer que les résultats étaient identiques. En particulier, je me suis concentré sur l'ordre des colonnes et Hour sous la forme d'une chaîne de caractères.

Fonctions

def jez(s):
    a = s.index.strftime('%H')
    b = s.index.strftime('%a')
    c = s.dt.floor('T').dt.total_seconds().div(60).astype(int)
    return pd.DataFrame({'hour':a,'day':b,'minute':c.values}, 
                        columns=['hour','day','minute'])

def pir1(s):
    return pd.DataFrame(
        np.core.defchararray.split(s.index.strftime('%H %a')).tolist(),
        columns=['hour', 'day']
    ).assign(minute=(s.dt.seconds // 60).values)

def pir2(s):
    return pd.DataFrame([dict(
        hour=f'{i.hour:02d}',
        day=i.strftime('%a'),
        minute=v.seconds // 60
    ) for i, v in s.items()], columns=['hour', 'day', 'minute'])

def pir3(s):
    a = np.array('Mon Tue Wed Thu Fri Sat Sun'.split())

    return pd.DataFrame(dict(
        hour=s.index.hour.astype(str).str.zfill(2),
        day=a[s.index.weekday],
        minute=s.values.astype('timedelta64[m]').astype(int)
    ), columns=['hour', 'day', 'minute'])

Retour Test

res = pd.DataFrame(
    np.nan,
    [10, 30, 100, 300, 1000, 3000, 10000, 30000],
    'jez pir1 pir2 pir3'.split()
)

for i in res.index:
    start = pd.to_datetime("2007-02-21 22:32:41", infer_datetime_format=True)
    rng = pd.date_range(start.floor('h'), periods=i, freq='h')
    end = rng.max() + pd.to_timedelta("01:32:41")
    left = pd.Series(rng, index=rng).clip_lower(start)
    right = pd.Series(rng + 1, index=rng).clip_upper(end)
    s = right - left
    for j in res.columns:
        stmt = f'{j}(s)'
        setp = f'from __main__ import {j}, s'
        res.at[i, j] = timeit(stmt, setp, number=100)

Résultats

res.plot(loglog=True)

enter image description here

res.div(res.min(1), 0)

             jez       pir1       pir2      pir3
10      2.364757   1.922064   1.000000  1.124539
30      1.916160   2.092680   1.129115  1.000000
100     3.039881   3.361606   2.180457  1.000000
300     3.967504   5.025567   3.920143  1.000000
1000    7.106132   9.757840   7.607425  1.000000
3000   10.104004  14.741414  11.957978  1.000000
10000  10.522324  15.318158  13.262373  1.000000
30000  11.804760  16.718153  14.289628  1.000000

Conclusions

Dans le graphique, vous pouvez voir que jez , pir1 y pir2 sont toutes regroupées lorsqu'elles sont représentées dans l'espace logarithmique. Cela nous indique que leur temps augmente dans le même ordre de grandeur. Cependant, pir3 a une grande séparation et s'agrandit avec des données plus importantes. La complexité temporelle de pir3 est plus petit et indique un avantage beaucoup plus important.

Cela devient plus clair lorsque nous examinons le tableau des multiples. Chaque ligne a une valeur minimale de 1 qui indique le temps le plus rapide. Toutes les autres valeurs de cette ligne sont des multiples du temps nécessaire pour accomplir la même tâche. En d'autres termes, plus la valeur est grande, plus le temps est lent. Plus la valeur est élevée, plus la méthode est lente. Comme vous pouvez le constater, ces multiples sont de plus en plus importants lorsque les données sont plus volumineuses. Cela signifie que l'avantage de pir3 s'améliore de plus en plus.

C'est à cela que ressemble le mieux. Il est inutile de se vanter d'une amélioration de 25 % du temps. À moins de disposer d'améliorations de l'ordre de grandeur, il est inutile d'essayer de convaincre les lecteurs qu'un algorithme ou une approche est "meilleur".

1voto

jezrael Points 290608

Je pense que vous avez besoin DatetimeIndex.strftime pour les heures et les jours de la semaine, ainsi que pour les minutes à partir de timedeltas, utiliser Series.dt.floor + Series.dt.total_seconds :

a = s.index.strftime('%H')
b = s.index.strftime('%a')
c = s.dt.floor('T').dt.total_seconds().div(60).astype(int)
#alternative
#c = s.dt.total_seconds().floordiv(60).astype(int)
df = pd.DataFrame({'hour':a,'day':b,'minute':c.values}, 
                  columns=['hour','day','minute'])
print (df)
  hour  day  minute
0   22  Tue      27
1   23  Tue      60
2   00  Wed      60
3   01  Wed      32

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X