Czy Pandy mogą wykreślić histogram dat?

101

Wziąłem serię i wymusiłem na niej kolumnę z datetime64[ns]datą i godziną dtype = (chociaż potrzebuję tylko rozdzielczości dziennej ... nie wiem, jak to zmienić).

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

ale kreślenie nie działa:

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

Chciałbym wykreślić histogram, który po prostu pokazuje liczbę dat według tygodnia, miesiąca lub roku .

Na pewno jest na to sposób pandas?

lollercoaster
źródło
2
czy możesz pokazać próbkę posiadanego df?
jrjc

Odpowiedzi:

164

Biorąc pod uwagę ten 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

a jeśli jeszcze nie jest:

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

Aby wyświetlić liczbę dat według miesiąca:

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

.dt umożliwia dostęp do właściwości daty i godziny.

Co da ci:

miesiąc według daty grupowania

Możesz zamienić miesiąc na rok, dzień itp.

Jeśli chcesz na przykład rozróżnić rok i miesiąc, po prostu wykonaj:

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

Co daje:

data grupowania miesiąc rok

Czy tego chciałeś? Czy to jasne?

Mam nadzieję że to pomoże !

jrjc
źródło
1
Jeśli masz dane obejmujące kilka lat, wszystkie dane ze stycznia są umieszczane w tej samej kolumnie i tak dalej dla każdego miesiąca.
drevicko
Działa, ale dla mnie (pandy 0.15.2) daty muszą być zapisane wielką literą D: df.groupby (df.Date.dt.month) .count (). Plot (kind = "bar")
harbun
@drevicko: Wierzę, że jest to oczekiwane. @harbun: datelub Datetutaj są nazwy kolumn, więc jeśli twoja kolumna z datami nazywa się foo, będzie to:df.foo.dt.month
jrjc
@jeanrjc Patrząc ponownie na pytanie, myślę, że masz rację. Dla innych, takich jak ja, którzy również potrzebują rozróżnienia na podstawie lat, czy istnieje prosty sposób groupbyna kombinację dwóch atrybutów danych kolumn (np .: rok i data)?
drevicko
Czy istnieje sposób na przygotowanie dat, aby móc użyć funkcji seaborn.distplot () do wykreślenia histogramu dat względem dat?
panc
11

Myślę, że resample może być tym, czego szukasz. W twoim przypadku zrób:

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

To tylko liczenie, a nie fabuła, więc musisz wtedy tworzyć własne wykresy.

Zobacz ten post, aby uzyskać więcej informacji na temat dokumentacji ponownego próbkowania pandas resample

Napotkałem podobne problemy jak ty. Mam nadzieję że to pomoże.

Ethan
źródło
2
howjest przestarzałe. Nowa składnia todf.resample('1M').count()
Dan Weaver,
6

Renderowany przykład

wprowadź opis obrazu tutaj

Przykładowy kod

#!/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
źródło
5

Udało mi się to obejść, (1) kreśląc za pomocą matplotlib zamiast bezpośrednio używać ramki danych i (2) używając valuesatrybutu. Zobacz przykład:

import matplotlib.pyplot as plt

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

To nie działa, jeśli nie używam values, ale nie wiem, dlaczego działa.

abeboparebop
źródło
3

Oto rozwiązanie, gdy chcesz mieć histogram, jakiego oczekujesz. Nie używa to grupowania, ale konwertuje wartości daty i godziny na liczby całkowite i zmienia etykiety na wykresie. Można by wprowadzić pewne ulepszenia, aby przenieść etykiety znaczników nawet w lokalizacje. Z podejściem możliwy jest również wykres oceny gęstości jądra (i każdy inny wykres).

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

Histogram daty i godziny

JulianWgs
źródło
1

Myślę, że aby rozwiązać ten problem, możesz użyć tego kodu, który konwertuje typ daty na typy int:

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

aby uzyskać tylko datę, możesz dodać ten kod:

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

źródło
1
to nie odpowiada na pytanie, jak wykreślić uporządkowany histogram z datą i godziną?
lollercoaster
Myślę, że twój problem z typem datetime, musisz znormalizować przed wykreśleniem
Możesz również zobaczyć ten link
1

Ja też miałem z tym problem. Wyobrażam sobie, że skoro pracujesz z datami, chcesz zachować porządek chronologiczny (tak jak ja).

W takim przypadku obejście to

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

Proszę, jeśli ktoś zna lepszy sposób, proszę o głos.

EDYCJA: dla Jean powyżej, oto próbka danych [Próbowałem losowo z pełnego zestawu danych, stąd trywialne dane histogramu.]

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

Wynik:

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
Zaprojektowany E.
źródło
1

Wszystkie te odpowiedzi wydają się zbyt skomplikowane, przynajmniej w przypadku „nowoczesnych” pand są to dwie linie.

df.set_index('date', inplace=True)
df.resample('M').size().plot.bar()
Briford Wylie
źródło
1
Wydaje się, że działa tylko wtedy, gdy masz DataFrame, ale nie wtedy, gdy wszystko, co masz, to Series. Czy rozważyłbyś dodanie notatki w tej sprawie?
David Z