Les pandas peuvent-ils reconnaître automatiquement les dates?

151

Aujourd'hui, j'ai été positivement surpris par le fait qu'en lisant des données à partir d'un fichier de données (par exemple), pandas est capable de reconnaître des 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 entiers, les flottants et les chaînes ont été reconnus correctement. Cependant, j'ai une colonne qui a des dates dans le format suivant: 2013-6-4. Ces dates étaient reconnues comme des chaînes (pas comme des objets de date python). Existe-t-il un moyen «d'apprendre» les pandas aux dates reconnues?

romain
la source
Veuillez toujours indiquer la version de pandas, pour ce genre de question dépendant de la version. En juillet 2013, cela aurait été v0.11
smci
Et les dtypes sont fixes pour chaque colonne, vous n'avez pas besoin de les parcourir df.iterrows()et de les afficher pour chaque ligne, faites df.info()une seule fois.
smci

Réponses:

327

Vous devriez ajouter parse_dates=True, ou parse_dates=['column name']lors de la lecture, c'est généralement suffisant pour l'analyser par magie. Mais il y a toujours des formats étranges qui doivent être définis manuellement. Dans un tel cas, vous pouvez également ajouter une fonction d'analyseur de date, qui est la manière la plus flexible 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, cela fusionne une colonne 'date' et une colonne 'time' en 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 strptimeet strftime dans cette page .

Rutger Kassies
la source
8
Cela n'a pas fonctionné pour moi, j'ai eu l'erreur suivante:TypeError: strptime() argument 1 must be str, not float
Jean Paul
6
J'ai eu cette erreur parce qu'il y avait nan dans ma trame de données.
Jean Paul
pouvez-vous ajouter un élément qui NaTs le matériau non analysable ou NaN ou / Ns. car il semble que cet analyseur ignore totalement toute la colonne si quelque chose comme ça est présent
Amir
Il existe une option infer_datetime_format: "pandas tentera de déduire le format des chaînes datetime dans les colonnes". Cela peut être utilisé à la place de date_parser.
Winand
1
Notez que si vos dates sont au ISO 8601format, vous ne devriez pas passer infer_datetime_formatou une fonction d'analyse syntaxique - c'est beaucoup plus lent que de laisser les pandas le gérer (en particulier ce dernier). Le format de date dans cette réponse appartient également à cette catégorie
Mr_and_Mrs_D
20

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 date_parserfonction reçoit une liste de dates au lieu d'une seule valeur. Dans ce cas, son code doit être mis à jour comme ceci:

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)
Sean
la source
11

La méthode pandas read_csv 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 être biaisée vers les formats de date nord-américains. Si vous habitez ailleurs, vous pourriez parfois être surpris par les résultats. Pour autant que je me souvienne, le 1/6/2000 signifie le 6 janvier aux États-Unis par opposition au 1 juin où je vis. Il est assez intelligent de les balancer si des dates comme le 23/6/2000 sont utilisées. Probablement plus sûr de rester avec les variations de date AAAAMMJJ. Toutes mes excuses aux développeurs de 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 de conversion de 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.
Joop
la source
2
Vous pouvez spécifier dayfirstTrue pour les dates européennes / internationales. pandas.pydata.org/pandas-docs/stable/generated/…
Will Gordon
10

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

Si une colonne ou un index contient une date impossible à analyser, la colonne ou l'index entier sera renvoyé tel quel en tant que type de données d'objet. Pour une analyse datetime non standard, utilisez pd.to_datetimeafter 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
Eugène Yarmash
la source
il convertit également d'autres colonnes en date, qui sont de type objet
ratnesh
10

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

Les travaux suivants:

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)
Je suis le morse
la source
1
J'utilise pandas 0.22 et j'accepte que la réponse acceptée ne fonctionne plus.
Dai
Cela crée un "TypeError: ne peut concaténer que str (pas" float ") à str" pour moi. La colonne de date est j / m / a et la colonne d'heure est H: M: 00
IceQueeny
8

Oui - selon la pandas.read_csv documentation :

Remarque: il existe un chemin rapide pour les dates au format iso8601 .

Donc, si votre csv a une colonne nommée datetimeet que les dates ressemblent à, 2013-01-01T01:01par exemple, l'exécuter fera en sorte que les pandas (je suis sur la v0.19.2) prennent automatiquement la date et l'heure:

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

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

Vérifier avec:

df.dtypes

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

Gaurav
la source
Je pense que vous avez mal compris la question. L'utilisateur est curieux de savoir si l'option pourrait être activée pour son format de chaîne.
Arya McCarthy
@AryaMcCarthy umm, il veut fondamentalement que la date soit reconnue correctement, alors je mentionne comment peut-il transformer les données sources afin qu'elles soient naturellement reconnues par les pandas. Nulle part il ne mentionne qu'il ne peut pas changer le format des données sources.
Gaurav
1

Si la performance compte pour vous, assurez-vous de chronométrer:

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

imprime:

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:%Sest apparemment une date au format iso8601, je suppose que le T peut être supprimé et remplacé par un espace), vous ne devriez pas spécifier infer_datetime_format(ce qui ne fait pas de différence avec les plus courants non plus apparemment) et en passant le vôtre parser dans juste paralyse les performances. D'un autre côté, date_parsercela fait une différence avec des formats de jour pas si standard. Assurez-vous de l'heure avant d'optimiser, comme d'habitude.

Mr_and_Mrs_D
la source
1

Lors du chargement du fichier csv, il contient une colonne de date.Nous avons deux approches pour que les pandas reconnaissent la colonne de date, c'est-à-dire

  1. Les pandas reconnaissent explicitement le format par arg date_parser=mydateparser

  2. Les pandas reconnaissent implicitement le format par agr infer_datetime_format=True

Certaines des données de la colonne de date

01/01/18

01/02/18

Ici, nous ne savons pas les deux premières choses. Cela peut être un mois ou un jour. Donc, dans ce cas, nous devons utiliser la méthode 1: - Passez explicitement le format

    mydateparser = lambda x: pd.datetime.strptime(x, "%m/%d/%y")
    df = pd.read_csv(file_name, parse_dates=['date_col_name'],
date_parser=mydateparser)

Méthode 2: - Reconnaître implicitement ou automatiquement le format

df = pd.read_csv(file_name, parse_dates=[date_col_name],infer_datetime_format=True)
kamran kausar
la source