Les pandas peuvent-ils tracer un histogramme des dates?

104

J'ai pris ma série et l'ai forcée à une colonne datetime de dtype = datetime64[ns](bien que seule la résolution du jour soit nécessaire ... je ne sais pas comment changer).

import pandas as pd
df = pd.read_csv('somefile.csv')
column = df['date']
column = pd.to_datetime(column, coerce=True)

mais le traçage ne fonctionne pas:

ipdb> column.plot(kind='hist')
*** TypeError: ufunc add cannot use operands with types dtype('<M8[ns]') and dtype('float64')

Je voudrais tracer un histogramme qui montre simplement le nombre de dates par semaine, mois ou année .

Il y a sûrement un moyen de faire cela pandas?

sucette
la source
2
pouvez-vous montrer un échantillon du df que vous avez?
jrjc

Réponses:

169

Compte tenu de ce df:

        date
0 2001-08-10
1 2002-08-31
2 2003-08-29
3 2006-06-21
4 2002-03-27
5 2003-07-14
6 2004-06-15
7 2003-08-14
8 2003-07-29

et, si ce n'est pas déjà le cas:

df["date"] = df["date"].astype("datetime64")

Pour afficher le nombre de dates par mois:

df.groupby(df["date"].dt.month).count().plot(kind="bar")

.dt vous permet d'accéder aux propriétés datetime.

Ce qui vous donnera:

groupby date mois

Vous pouvez remplacer mois par année, jour, etc.

Si vous voulez distinguer l'année et le mois par exemple, faites simplement:

df.groupby([df["date"].dt.year, df["date"].dt.month]).count().plot(kind="bar")

Qui donne:

groupby date mois année

C'était ce que tu voulais? Est-ce clair?

J'espère que cela t'aides !

jrjc
la source
1
Si vous avez des données couvrant plusieurs années, toutes les données de «janvier» sont placées dans la même colonne et ainsi de suite pour chaque mois.
drevicko
Fonctionne, mais pour moi (pandas 0.15.2) les dates doivent être écrites avec D majuscule: df.groupby (df.Date.dt.month) .count (). Plot (kind = "bar")
harbun
@drevicko: C'est normal, je crois. @harbun: dateou Datevoici les noms de colonnes, donc si votre colonne avec des dates s'appelle foo, ce serait:df.foo.dt.month
jrjc
@jeanrjc En repensant à la question, je suppose que vous avez raison. Pour d'autres comme moi qui ont besoin de faire une distinction par année également, existe-t-il un moyen simple de groupbycombiner deux attributs de données de colonnes (par exemple: année et date)?
drevicko le
Existe-t-il un moyen de préparer les dates afin que je puisse utiliser seaborn.distplot () pour tracer l'histogramme des dates sur les dates?
panc
11

Je pense que le rééchantillonnage est peut-être ce que vous recherchez. Dans votre cas, faites:

df.set_index('date', inplace=True)
# for '1M' for 1 month; '1W' for 1 week; check documentation on offset alias
df.resample('1M', how='count')

Il ne fait que compter et non l'intrigue, vous devez donc créer vos propres parcelles.

Voir cet article pour plus de détails sur la documentation de la documentation de resample pandas resample

J'ai rencontré des problèmes similaires à ceux que vous avez rencontrés. J'espère que cela t'aides.

Ethan
la source
2
howest obsolète. La nouvelle syntaxe estdf.resample('1M').count()
Dan Weaver
6

Exemple rendu

entrez la description de l'image ici

Exemple de code

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""Create random datetime object."""

# core modules
from datetime import datetime
import random

# 3rd party modules
import pandas as pd
import matplotlib.pyplot as plt


def visualize(df, column_name='start_date', color='#494949', title=''):
    """
    Visualize a dataframe with a date column.

    Parameters
    ----------
    df : Pandas dataframe
    column_name : str
        Column to visualize
    color : str
    title : str
    """
    plt.figure(figsize=(20, 10))
    ax = (df[column_name].groupby(df[column_name].dt.hour)
                         .count()).plot(kind="bar", color=color)
    ax.set_facecolor('#eeeeee')
    ax.set_xlabel("hour of the day")
    ax.set_ylabel("count")
    ax.set_title(title)
    plt.show()


def create_random_datetime(from_date, to_date, rand_type='uniform'):
    """
    Create random date within timeframe.

    Parameters
    ----------
    from_date : datetime object
    to_date : datetime object
    rand_type : {'uniform'}

    Examples
    --------
    >>> random.seed(28041990)
    >>> create_random_datetime(datetime(1990, 4, 28), datetime(2000, 12, 31))
    datetime.datetime(1998, 12, 13, 23, 38, 0, 121628)
    >>> create_random_datetime(datetime(1990, 4, 28), datetime(2000, 12, 31))
    datetime.datetime(2000, 3, 19, 19, 24, 31, 193940)
    """
    delta = to_date - from_date
    if rand_type == 'uniform':
        rand = random.random()
    else:
        raise NotImplementedError('Unknown random mode \'{}\''
                                  .format(rand_type))
    return from_date + rand * delta


def create_df(n=1000):
    """Create a Pandas dataframe with datetime objects."""
    from_date = datetime(1990, 4, 28)
    to_date = datetime(2000, 12, 31)
    sales = [create_random_datetime(from_date, to_date) for _ in range(n)]
    df = pd.DataFrame({'start_date': sales})
    return df


if __name__ == '__main__':
    import doctest
    doctest.testmod()
    df = create_df()
    visualize(df)
Martin Thoma
la source
5

J'ai pu contourner ce problème en (1) traçant avec matplotlib au lieu d'utiliser directement le dataframe et (2) en utilisant l' valuesattribut. Voir exemple:

import matplotlib.pyplot as plt

ax = plt.gca()
ax.hist(column.values)

Cela ne fonctionne pas si je ne l'utilise pas values, mais je ne sais pas pourquoi cela fonctionne.

abeboparebop
la source
3

Voici une solution lorsque vous souhaitez simplement avoir un histogramme comme vous l'attendez. Cela n'utilise pas groupby, mais convertit les valeurs de date / heure en nombres entiers et modifie les étiquettes sur le tracé. Certaines améliorations pourraient être apportées pour déplacer les étiquettes de graduation vers des emplacements pairs. De plus, avec l'approche, un graphique d'estimation de la densité du noyau (et tout autre graphique) est également possible.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

df = pd.DataFrame({"datetime": pd.to_datetime(np.random.randint(1582800000000000000, 1583500000000000000, 100, dtype=np.int64))})
fig, ax = plt.subplots()
df["datetime"].astype(np.int64).plot.hist(ax=ax)
labels = ax.get_xticks().tolist()
labels = pd.to_datetime(labels)
ax.set_xticklabels(labels, rotation=90)
plt.show()

Histogramme Datetime

JulianWgs
la source
1

Je pense que pour résoudre ce problème, vous pouvez utiliser ce code, il convertit le type de date en types int:

df['date'] = df['date'].astype(int)
df['date'] = pd.to_datetime(df['date'], unit='s')

pour obtenir la date uniquement, vous pouvez ajouter ce code:

pd.DatetimeIndex(df.date).normalize()
df['date'] = pd.DatetimeIndex(df.date).normalize()

la source
1
cela ne répond pas à la question de savoir comment tracer un histogramme datetime ordonné?
lollercoaster
Je pense que votre problème au type datetime, vous devez normaliser avant de tracer
Vous pouvez également voir ce lien
1

J'avais juste des problèmes avec ça aussi. J'imagine que puisque vous travaillez avec des dates, vous souhaitez conserver l'ordre chronologique (comme je l'ai fait.)

La solution de contournement est alors

import matplotlib.pyplot as plt    
counts = df['date'].value_counts(sort=False)
plt.bar(counts.index,counts)
plt.show()

S'il vous plaît, si quelqu'un connaît un meilleur moyen, veuillez parler.

EDIT: pour jean ci-dessus, voici un échantillon des données [j'ai échantillonné au hasard à partir de l'ensemble de données complet, d'où les données d'histogramme triviales.]

print dates
type(dates),type(dates[0])
dates.hist()
plt.show()

Production:

0    2001-07-10
1    2002-05-31
2    2003-08-29
3    2006-06-21
4    2002-03-27
5    2003-07-14
6    2004-06-15
7    2002-01-17
Name: Date, dtype: object
<class 'pandas.core.series.Series'> <type 'datetime.date'>

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-38-f39e334eece0> in <module>()
      2 print dates
      3 print type(dates),type(dates[0])
----> 4 dates.hist()
      5 plt.show()

/anaconda/lib/python2.7/site-packages/pandas/tools/plotting.pyc in hist_series(self, by, ax, grid, xlabelsize, xrot, ylabelsize, yrot, figsize, bins, **kwds)
   2570         values = self.dropna().values
   2571 
-> 2572         ax.hist(values, bins=bins, **kwds)
   2573         ax.grid(grid)
   2574         axes = np.array([ax])

/anaconda/lib/python2.7/site-packages/matplotlib/axes/_axes.pyc in hist(self, x, bins, range, normed, weights, cumulative, bottom, histtype, align, orientation, rwidth, log, color, label, stacked, **kwargs)
   5620             for xi in x:
   5621                 if len(xi) > 0:
-> 5622                     xmin = min(xmin, xi.min())
   5623                     xmax = max(xmax, xi.max())
   5624             bin_range = (xmin, xmax)

TypeError: can't compare datetime.date to float
ConçuE
la source
1

Toutes ces réponses semblent trop complexes, du moins avec les pandas `` modernes '', ce sont deux lignes.

df.set_index('date', inplace=True)
df.resample('M').size().plot.bar()
Briford Wylie
la source
1
Cela semble fonctionner uniquement si vous avez un fichier DataFrame, mais pas si tout ce que vous avez est un fichier Series. Envisageriez-vous d'ajouter une note sur cette affaire?
David Z