web-dev-qa-db-fra.com

Analyser une colonne Pandas vers Datetime

J'ai un DataFrame avec une colonne nommée date. Comment convertir/analyser la colonne 'date' en un objet DateTime?

J'ai chargé la colonne de date à partir d'une base de données Postgresql en utilisant sql.read_frame(). Un exemple de la colonne date est 2013-04-04.

Ce que j'essaie de faire, c'est de sélectionner toutes les lignes d'une trame de données qui ont leurs colonnes de date dans une certaine période, comme après 2013-04-01 Et avant 2013-04-04.

Ma tentative ci-dessous donne l'erreur 'Series' object has no attribute 'read'

Tentative

import dateutil

df['date'] = dateutil.parser.parse(df['date'])

Erreur

AttributeError                            Traceback (most recent call last)
<ipython-input-636-9b19aa5f989c> in <module>()
     15 
     16 # Parse 'Date' Column to Datetime
---> 17 df['date'] = dateutil.parser.parse(df['date'])
     18 
     19 # SELECT RECENT SALES

C:\Python27\lib\site-packages\dateutil\parser.pyc in parse(timestr, parserinfo, **kwargs)
    695         return parser(parserinfo).parse(timestr, **kwargs)
    696     else:
--> 697         return DEFAULTPARSER.parse(timestr, **kwargs)
    698 
    699 

C:\Python27\lib\site-packages\dateutil\parser.pyc in parse(self, timestr, default, ignoretz, tzinfos, **kwargs)
    299             default = datetime.datetime.now().replace(hour=0, minute=0,
    300                                                       second=0, microsecond=0)
--> 301         res = self._parse(timestr, **kwargs)
    302         if res is None:
    303             raise ValueError, "unknown string format"

C:\Python27\lib\site-packages\dateutil\parser.pyc in _parse(self, timestr, dayfirst, yearfirst, fuzzy)
    347             yearfirst = info.yearfirst
    348         res = self._result()
--> 349         l = _timelex.split(timestr)
    350         try:
    351 

C:\Python27\lib\site-packages\dateutil\parser.pyc in split(cls, s)
    141 
    142     def split(cls, s):
--> 143         return list(cls(s))
    144     split = classmethod(split)
    145 

C:\Python27\lib\site-packages\dateutil\parser.pyc in next(self)
    135 
    136     def next(self):
--> 137         token = self.get_token()
    138         if token is None:
    139             raise StopIteration

C:\Python27\lib\site-packages\dateutil\parser.pyc in get_token(self)
     66                 nextchar = self.charstack.pop(0)
     67             else:
---> 68                 nextchar = self.instream.read(1)
     69                 while nextchar == '\x00':
     70                     nextchar = self.instream.read(1)

AttributeError: 'Series' object has no attribute 'read'

df['date'].apply(dateutil.parser.parse) me donne l'erreur AttributeError: 'datetime.date' object has no attribute 'read'

df['date'].truncate(after='2013/04/01') donne l'erreur TypeError: can't compare datetime.datetime to long

df['date'].dtype Renvoie dtype('O'). Est-ce déjà un objet datetime?

26
Nyxynyx

pandas le lit déjà comme un objet datetime! Donc, ce que vous voulez, c'est sélectionner des lignes entre deux dates et vous pouvez le faire en masquant:

df_masked = df[(df.date > '2012-04-01') & (df.date < '2012-04-04')]

Parce que vous avez dit que vous obteniez une erreur de la chaîne pour une raison quelconque, essayez ceci:

df_masked = df[(df.date > datetime.date(2012,4,1)) & (df.date < datetime.date(2012,4,4))]
5
Ryan Saxe

Pandas connaît la date et l'heure de l'objet, mais lorsque vous utilisez certaines des fonctions d'importation, il est considéré comme une chaîne. Donc, ce que vous devez faire est de vous assurer que la colonne est définie comme le type datetime et non comme une chaîne. Ensuite, vous pouvez faire votre requête.

df['date']  = pd.to_datetime(df['date'])
df_masked = df[(df['date'] > datetime.date(2012,4,1)) & (df['date'] < datetime.date(2012,4,4))]
53
Keith

Vous avez probablement besoin de apply, donc quelque chose comme:

df['date'] = df['date'].apply(dateutil.parser.parse)

Sans un exemple de colonne, je ne peux pas garantir que cela fonctionnera, mais quelque chose dans ce sens devrait vous aider à continuer.

4
herrfz

Ne confondez pas datetime.date avec Pandas pd.Timestamp

Une "série Pandas datetime" contient pd.Timestamp éléments, pas datetime.date éléments. La solution recommandée pour Pandas:

s = pd.to_datetime(s)    # convert series to Pandas
mask = s > '2018-03-10'  # calculate Boolean mask against Pandas-compatible object

Les meilleures réponses ont des problèmes:

  • @ La première tentative de réponse acceptée de RyanSaxe ne fonctionne pas; la deuxième réponse est inefficace.
  • À partir de Pandas v0.23.0, la réponse très appréciée de @ Keith ne fonctionne pas; elle donne TypeError.

Toute bonne Pandas doit assurer:

  1. La série est un Pandas datetime series, pas object dtype.
  2. La série datetime est comparée à un objet compatible, par ex. pd.Timestamp, ou chaîne au format correct.

Voici une démo avec analyse comparative, démontrant que le coût unique de la conversion peut être immédiatement compensé par une seule opération:

from datetime import date

L = [date(2018, 1, 10), date(2018, 5, 20), date(2018, 10, 30), date(2018, 11, 11)]
s = pd.Series(L*10**5)

a = s > date(2018, 3, 10)             # accepted solution #2, inefficient
b = pd.to_datetime(s) > '2018-03-10'  # more efficient, including datetime conversion

assert a.equals(b)                    # check solutions give same result

%timeit s > date(2018, 3, 10)                  # 40.5 ms
%timeit pd.to_datetime(s) > '2018-03-10'       # 33.7 ms

s = pd.to_datetime(s)

%timeit s > '2018-03-10'                       # 2.85 ms
4
jpp

Vous devez parcourir les éléments et les analyser indépendamment, puis construire une nouvelle liste.

df['date'] = [dateutil.parser.parse(x) for x in df['date']]
1
ryzhiy