web-dev-qa-db-fra.com

Est-ce que pandas peut reconnaître automatiquement les dates?

Aujourd'hui, j'ai été positivement surpris par le fait que lors de la lecture de données à partir d'un fichier de données (par exemple), pandas est capable de reconnaître les types de valeurs:

df = pandas.read_csv('test.dat', delimiter=r"\s+", names=['col1','col2','col3'])

Par exemple, il peut être vérifié de cette manière:

for i, r in df.iterrows():
    print type(r['col1']), type(r['col2']), type(r['col3'])

En particulier, les nombres flottants et les chaînes ont été correctement reconnus. Cependant, j'ai une colonne qui a des dates au format suivant: 2013-6-4. Ces dates ont été reconnues comme des chaînes (et non comme python objets_date). Existe-t-il un moyen "d'apprendre" pandas aux dates reconnues?

114
Roman

Vous devez ajouter parse_dates=True ou parse_dates=['column name'] lors de la lecture, ce qui est généralement suffisant pour l’analyser comme par magie. Mais il y a toujours des formats étranges qui doivent être définis manuellement. Dans ce cas, vous pouvez également ajouter une fonction d'analyse de la date, qui est la plus souple possible.

Supposons que vous ayez une colonne 'datetime' avec votre chaîne, alors:

dateparse = lambda x: pd.datetime.strptime(x, '%Y-%m-%d %H:%M:%S')

df = pd.read_csv(infile, parse_dates=['datetime'], date_parser=dateparse)

De cette façon, vous pouvez même combiner plusieurs colonnes dans une seule colonne datetime. Ceci fusionne une colonne "date" et une colonne "heure" dans une seule colonne "datetime":

dateparse = lambda x: pd.datetime.strptime(x, '%Y-%m-%d %H:%M:%S')

df = pd.read_csv(infile, parse_dates={'datetime': ['date', 'time']}, date_parser=dateparse)

Vous pouvez trouver des directives (c'est-à-dire les lettres à utiliser pour différents formats) pour strptime et strftimedans cette page .

253
Rutger Kassies

Peut-être que l'interface pandas a changé depuis la réponse de @Rutger, mais dans la version que j'utilise (0.15.2), la fonction date_parser reçoit une liste de dates au lieu d'une valeur unique. Dans ce cas, son code devrait être mis à jour comme suit:

dateparse = lambda dates: [pd.datetime.strptime(d, '%Y-%m-%d %H:%M:%S') for d in dates]

df = pd.read_csv(infile, parse_dates=['datetime'], date_parser=dateparse)
15
Sean

la méthode read_csv de pandas est idéale pour analyser les dates. Documentation complète sur http://pandas.pydata.org/pandas-docs/stable/generated/pandas.io.parsers.read_csv.html

vous pouvez même avoir les différentes parties de date dans différentes colonnes et passer le paramètre:

parse_dates : boolean, list of ints or names, list of lists, or dict
If True -> try parsing the index. If [1, 2, 3] -> try parsing columns 1, 2, 3 each as a
separate date column. If [[1, 3]] -> combine columns 1 and 3 and parse as a single date
column. {‘foo’ : [1, 3]} -> parse columns 1, 3 as date and call result ‘foo’

La détection par défaut des dates fonctionne très bien, mais elle semble privilégier les formats de date nord-américains. Si vous habitez ailleurs, vous risquez parfois d'être surpris par les résultats. Autant que je me souvienne, 1/6/2000 signifie le 6 janvier aux États-Unis, par opposition au 1 er juin où je vis. Il est assez malin de les faire tourner si des dates comme 23/6/2000 sont utilisées. Probablement plus sûr de rester avec les variations AAAAMMJJ de la date si. Toutes mes excuses aux développeurs pandas, ici, mais je ne l’ai pas testé avec des dates locales récemment.

vous pouvez utiliser le paramètre date_parser pour transmettre une fonction permettant de convertir votre format.

date_parser : function
Function to use for converting a sequence of string columns to an array of datetime
instances. The default uses dateutil.parser.parser to do the conversion.
11
Joop

Lors de la fusion de deux colonnes en une seule colonne datetime, la réponse acceptée génère une erreur (version pandas 0.20.3), car les colonnes sont envoyées séparément à la fonction date_parser.

Les oeuvres suivantes:

def dateparse(d,t):
    dt = d + " " + t
    return pd.datetime.strptime(dt, '%d/%m/%Y %H:%M:%S')

df = pd.read_csv(infile, parse_dates={'datetime': ['date', 'time']}, date_parser=dateparse)
8
IamTheWalrus

Oui - selon le pandas.read_csvdocumentation :

Remarque: Il existe un raccourci pour les dates formaté en iso8601 .

Donc, si votre csv a une colonne nommée datetime et que les dates ressemblent à 2013-01-01T01:01, par exemple, cela permettra à pandas (je suis sur la v0.19.2) de choisir la date et le temps automatiquement:

df = pd.read_csv('test.csv', parse_dates=['datetime'])

Notez que vous devez explicitement passer parse_dates, cela ne fonctionne pas sans.

Vérifiez avec:

df.dtypes

Vous devriez voir que le type de données de la colonne est datetime64[ns]

7
Gaurav

Vous pouvez utiliser pandas.to_datetime() comme recommandé dans la documentation de pandas.read_csv() :

Si une colonne ou un index contient une date non analysable, l'intégralité de la colonne ou de l'index est renvoyée sous forme de type de données d'objet. Pour une analyse datetime non standard, utilisez pd.to_datetime après pd.read_csv.

Démo:

>>> D = {'date': '2013-6-4'}
>>> df = pd.DataFrame(D, index=[0])
>>> df
       date
0  2013-6-4
>>> df.dtypes
date    object
dtype: object
>>> df['date'] = pd.to_datetime(df.date, format='%Y-%m-%d')
>>> df
        date
0 2013-06-04
>>> df.dtypes
date    datetime64[ns]
dtype: object
7
Eugene Yarmash

Si la performance compte pour vous, assurez-vous de gagner du temps:

import sys
import timeit
import pandas as pd

print('Python %s on %s' % (sys.version, sys.platform))
print('Pandas version %s' % pd.__version__)

repeat = 3
numbers = 100

def time(statement, _setup=None):
    print (min(
        timeit.Timer(statement, setup=_setup or setup).repeat(
            repeat, numbers)))

print("Format %m/%d/%y")
setup = """import pandas as pd
import io

data = io.StringIO('''\
ProductCode,Date
''' + '''\
x1,07/29/15
x2,07/29/15
x3,07/29/15
x4,07/30/15
x5,07/29/15
x6,07/29/15
x7,07/29/15
y7,08/05/15
x8,08/05/15
z3,08/05/15
''' * 100)"""

time('pd.read_csv(data); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"]); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"],'
     'infer_datetime_format=True); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"],'
     'date_parser=lambda x: pd.datetime.strptime(x, "%m/%d/%y")); data.seek(0)')

print("Format %Y-%m-%d %H:%M:%S")
setup = """import pandas as pd
import io

data = io.StringIO('''\
ProductCode,Date
''' + '''\
x1,2016-10-15 00:00:43
x2,2016-10-15 00:00:56
x3,2016-10-15 00:00:56
x4,2016-10-15 00:00:12
x5,2016-10-15 00:00:34
x6,2016-10-15 00:00:55
x7,2016-10-15 00:00:06
y7,2016-10-15 00:00:01
x8,2016-10-15 00:00:00
z3,2016-10-15 00:00:02
''' * 1000)"""

time('pd.read_csv(data); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"]); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"],'
     'infer_datetime_format=True); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"],'
     'date_parser=lambda x: pd.datetime.strptime(x, "%Y-%m-%d %H:%M:%S")); data.seek(0)')

impressions:

Python 3.7.1 (v3.7.1:260ec2c36a, Oct 20 2018, 03:13:28) 
[Clang 6.0 (clang-600.0.57)] on darwin
Pandas version 0.23.4
Format %m/%d/%y
0.19123052499999993
8.20691274
8.143124389
1.2384357139999977
Format %Y-%m-%d %H:%M:%S
0.5238807110000039
0.9202787830000005
0.9832778819999959
12.002349824999996

Donc, avec la date au format iso8601 (%Y-%m-%d %H:%M:%S est apparemment une date au format iso8601, je suppose que le T peut être supprimé et remplacé par un espace), vous devriez ) not spécifiez infer_datetime_format (ce qui ne fait apparemment pas de différence avec les plus courants, apparemment) et transmettez votre propre analyseur syntaxique uniquement pour la performance estropiée. Par contre, date_parser fait une différence avec les formats de jour peu standard. Veillez à respecter le délai d’optimisation, comme d’habitude.

1
Mr_and_Mrs_D