¿Pueden los pandas trazar un histograma de fechas?

101

Tomé mi Serie y la forcé a una columna de fecha y hora de dtype = datetime64[ns](aunque solo necesito una resolución de día ... no estoy seguro de cómo cambiar).

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

pero trazar no funciona:

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

Me gustaría trazar un histograma que solo muestre el recuento de fechas por semana, mes o año .

Seguramente hay una forma de hacer esto en pandas?

lollercoaster
fuente
2
¿puede mostrar una muestra del df que tiene?
jrjc

Respuestas:

164

Dado este 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

y, si aún no es el caso:

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

Para mostrar el recuento de fechas por mes:

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

.dt le permite acceder a las propiedades de fecha y hora.

Que te dará:

grupo por fecha mes

Puede reemplazar mes por año, día, etc.

Si desea distinguir año y mes, por ejemplo, simplemente haga:

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

Lo que da:

grupo por fecha mes año

¿Era lo que querías? ¿Está claro esto?

Espero que esto ayude !

jrjc
fuente
1
Si tiene datos que abarcan varios años, todos los datos de 'enero' se colocan en la misma columna y así sucesivamente para cada mes.
drevicko
Funciona, pero para mí (pandas 0.15.2) las fechas deben escribirse con D mayúscula: df.groupby (df.Date.dt.month) .count (). Plot (kind = "bar")
harbun
@drevicko: Eso se esperaba, creo. @harbun: dateo Dateaquí están los nombres de las columnas, por lo que si su columna con fechas se llama foo, sería:df.foo.dt.month
jrjc
@jeanrjc Mirando de nuevo la pregunta, supongo que tienes razón. Para otros como yo, que también necesitan distinguir por años, ¿hay una forma sencilla groupbyde combinar dos atributos de los datos de una columna (por ejemplo, año y fecha)?
drevicko
¿Hay alguna forma de preparar las fechas para poder usar seaborn.distplot () para trazar el histograma de fechas sobre fechas?
panc
11

Creo que remuestrear puede ser lo que estás buscando. En su caso, haga:

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

Es solo hacer el conteo y no la trama, entonces tienes que hacer tus propias tramas.

Consulte esta publicación para obtener más detalles sobre la documentación de remuestrear pandas remuestrear la documentación

Me he encontrado con problemas similares a los tuyos. Espero que esto ayude.

Ethan
fuente
2
howes obsoleto. La nueva sintaxis esdf.resample('1M').count()
Dan Weaver
6

Ejemplo renderizado

ingrese la descripción de la imagen aquí

Código de ejemplo

#!/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)
Martín Thoma
fuente
5

Pude solucionar esto (1) trazando con matplotlib en lugar de usar el marco de datos directamente y (2) usando el valuesatributo. Ver ejemplo:

import matplotlib.pyplot as plt

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

Esto no funciona si no lo uso values, pero no sé por qué funciona.

abeboparebop
fuente
3

Aquí hay una solución para cuando solo desea tener un histograma como lo espera. Esto no usa groupby, pero convierte los valores de fecha y hora en números enteros y cambia las etiquetas en el gráfico. Se podrían realizar algunas mejoras para mover las etiquetas de las marcas a ubicaciones uniformes. También con el enfoque, también es posible una gráfica de estimación de densidad de kernel (y cualquier otra gráfica).

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

Histograma de fecha y hora

JulianWgs
fuente
1

Creo que para resolver ese problema, puede usar este código, convierte el tipo de fecha en tipos int:

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

solo para obtener la fecha, puede agregar este código:

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

fuente
1
esto no responde a la pregunta de cómo trazar un histograma de fecha y hora ordenado.
lollercoaster
Creo que su problema en el tipo de fecha y hora, debe normalizar antes de trazar
También puede ver este enlace
1

Yo también estaba teniendo problemas con esto. Me imagino que, dado que está trabajando con fechas, desea preservar el orden cronológico (como hice yo).

La solución alternativa es

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

Por favor, si alguien conoce una forma mejor, por favor hable.

EDITAR: para jean anterior, aquí hay una muestra de los datos [tomé una muestra aleatoria del conjunto de datos completo, de ahí los datos triviales del histograma].

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

Salida:

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
EngineeredE
fuente
1

Todas estas respuestas parecen demasiado complejas, al menos con los pandas 'modernos' son dos líneas.

df.set_index('date', inplace=True)
df.resample('M').size().plot.bar()
Briford Wylie
fuente
1
Esto parece funcionar solo si tiene un DataFrame, pero no si todo lo que tiene es un Series. ¿Consideraría agregar una nota sobre ese caso?
David Z