8 votes

timedelta vers un type de chaîne de caractères dans un dataframe pandas

J'ai un dataframe df et sa première colonne est timedelta64

df.info():

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 686 entries, 0 to 685
Data columns (total 6 columns):
0    686 non-null timedelta64[ns]
1    686 non-null object
2    686 non-null object
3    686 non-null object
4    686 non-null object
5    686 non-null object

Si je print(df[0][2]) par exemple, il me donnera 0 days 05:01:11 . Cependant, je ne veux pas que le 0 days classée. Je veux seulement 05:01:11 à imprimer. Quelqu'un pourrait-il m'apprendre à le faire ? Merci beaucoup !

11voto

jezrael Points 290608

Elle est possible grâce à :

df['duration1'] = df['duration'].astype(str).str[-18:-10]

Mais la solution n'est pas générale, si l'entrée est 3 days 05:01:11 il supprime 3 days aussi.

La solution ne fonctionne donc correctement que pour des durées inférieures à un jour.

La solution plus générale est la suivante créer un format personnalisé :

N = 10
np.random.seed(11230)
rng = pd.date_range('2017-04-03 15:30:00', periods=N, freq='13.5H')
df = pd.DataFrame({'duration': np.abs(np.random.choice(rng, size=N) - 
                                 np.random.choice(rng, size=N)) })  

df['duration1'] = df['duration'].astype(str).str[-18:-10]

def f(x):
    ts = x.total_seconds()
    hours, remainder = divmod(ts, 3600)
    minutes, seconds = divmod(remainder, 60)
    return ('{}:{:02d}:{:02d}').format(int(hours), int(minutes), int(seconds)) 

df['duration2'] = df['duration'].apply(f)
print (df)

         duration duration1  duration2
0 2 days 06:00:00  06:00:00   54:00:00
1 2 days 19:30:00  19:30:00   67:30:00
2 1 days 03:00:00  03:00:00   27:00:00
3 0 days 00:00:00  00:00:00    0:00:00
4 4 days 12:00:00  12:00:00  108:00:00
5 1 days 03:00:00  03:00:00   27:00:00
6 0 days 13:30:00  13:30:00   13:30:00
7 1 days 16:30:00  16:30:00   40:30:00
8 0 days 00:00:00  00:00:00    0:00:00
9 1 days 16:30:00  16:30:00   40:30:00

7voto

Simon G. Points 11

Voici une version courte et robuste utilisant apply() :

df['timediff_string'] = df['timediff'].apply(
    lambda x: f'{x.components.hours:02d}:{x.components.minutes:02d}:{x.components.seconds:02d}'
              if not pd.isnull(x) else ''
)

Il s'agit de tirer parti de la composants des objets pandas Timedelta et gère également les valeurs vides (NaT).

Si le différentiel de temps ne contient pas d'objets pandas Timedelta, vous pouvez la convertir :

df['timediff'] = pd.to_timedelta(df['timediff'])

3voto

jayreed1 Points 82

datetime.timedelta déjà les formats comme vous le souhaitez. Le nœud du problème est que Pandas convertit en interne en numpy.timedelta .

import pandas as pd
from datetime import timedelta

time_1 = timedelta(days=3, seconds=3400)
time_2 = timedelta(days=0, seconds=3400)
print(time_1)
print(time_2)

times = pd.Series([time_1, time_2])

# Times are converted to Numpy timedeltas.
print(times)

# Convert to string after converting to datetime.timedelta.
times = times.apply(
    lambda numpy_td: str(timedelta(seconds=numpy_td.total_seconds())))

print(times)

Il faut donc convertir en datetime.timedelta et ensuite str (pour empêcher la reconversion en numpy.timedelta ) avant l'impression.

3 days, 0:56:40
0:56:400

0   3 days 00:56:40
1   0 days 00:56:40
dtype: timedelta64[ns]

0    3 days, 0:56:40
1            0:56:40
dtype: object

Je suis venu ici pour chercher des réponses à la même question, et j'ai donc pensé qu'il fallait apporter des éclaircissements : )

1voto

Optimesh Points 212

Étant donné que l'OP est d'accord avec une colonne d'objets (un peu verbeuse) :

def splitter(td):

  td = str(td).split(' ')[-1:][0]

  return td

df['split'] = df['timediff'].apply(splitter)

En fait, nous prenons la colonne timedelta, transformons son contenu en une chaîne, puis divisons la chaîne (ce qui crée une liste) et prenons le dernier élément de cette liste, qui serait le composant hh:mm:ss.

Notez que le fait de spécifier ' ' pour savoir ce qu'il faut diviser est redondant ici.

Alternative one liner :

df['split2'] = df['timediff'].astype('str').str.split().str[-1]

qui est très similaire, mais pas très joli, IMHO. De plus, la sortie inclut les millisecondes, ce qui n'est pas le cas dans la première solution. Je ne suis pas sûr de la raison de cela (merci de commenter si vous la connaissez). Si vos données sont volumineuses, il peut être intéressant de tester ces différentes approches.

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