2015-03-29 19 views
18

Jest Pandy DataFrame:Pandy i matplotlib - fill_between() vs datetime64

<class 'pandas.core.frame.DataFrame'> 
Int64Index: 300 entries, 5220 to 5519 
Data columns (total 3 columns): 
Date    300 non-null datetime64[ns] 
A    300 non-null float64 
B    300 non-null float64 
dtypes: datetime64[ns](1), float64(2) 
memory usage: 30.5 KB 

Chcę wykreślić i serii B vs Date.

plt.plot_date(data['Date'], data['A'], '-') 
plt.plot_date(data['Date'], data['B'], '-') 

Następnie chcę zastosować fill_between() na obszarze pomiędzy A i B serii:

plt.fill_between(data['Date'], data['A'], data['B'], 
       where=data['A'] >= data['B'], 
       facecolor='green', alpha=0.2, interpolate=True) 

które wyjścia:

TypeError: ufunc 'isfinite' not supported for the input types, and the inputs 
could not be safely coerced to any supported types according to the casting 
rule ''safe'' 

Czy matplotlib przyjąć pandy datetime64 obiekt w fill_between() funkcji? Czy powinienem przekonwertować go na inny typ daty?

+0

To rozwiązanie powinno działać zarówno 'plt' i osi' plt.fill_between (dane [ 'data']. Dt.to_pydatetime(), dane [ 'A'] ... '. Konwertuje numpy datetime64 [ns] na datetime Pythona, że ​​fill_between rozumie. – jedi

Odpowiedz

15

Pandas registers a converter w matplotlib.units.registry który przekształca liczbę typów datetime (np PANDAS DatetimeIndex i NumPy tablice dtype datetime64) do matplotlib datenums, ale nie obsługuje Pandy Series z dtype datetime64.

In [67]: import pandas.tseries.converter as converter 

In [68]: c = converter.DatetimeConverter() 

In [69]: type(c.convert(df['Date'].values, None, None)) 
Out[69]: numpy.ndarray    # converted (good) 

In [70]: type(c.convert(df['Date'], None, None)) 
Out[70]: pandas.core.series.Series # left unchanged 

fill_between sprawdza i wykorzystuje konwerter do obsługi danych, jeśli istnieje.

Więc jako obejście, można konwertować daty do tablicy numpy z datetime64 dydaktycznego:

d = data['Date'].values 
plt.fill_between(d, data['A'], data['B'], 
       where=data['A'] >= data['B'], 
       facecolor='green', alpha=0.2, interpolate=True) 

Na przykład

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

N = 300 
dates = pd.date_range('2000-1-1', periods=N, freq='D') 
x = np.linspace(0, 2*np.pi, N) 
data = pd.DataFrame({'A': np.sin(x), 'B': np.cos(x), 
       'Date': dates}) 
plt.plot_date(data['Date'], data['A'], '-') 
plt.plot_date(data['Date'], data['B'], '-') 

d = data['Date'].values 
plt.fill_between(d, data['A'], data['B'], 
       where=data['A'] >= data['B'], 
       facecolor='green', alpha=0.2, interpolate=True) 
plt.xticks(rotation=25) 
plt.show() 

enter image description here

+0

Obejście działa idealnie - dziękuję bardzo.To zachowanie jest dość dziwne, co może być przyczyną tutaj? – chilliq

+3

@ chilliq: Moja pierwsza sugestia była do użycia a DatetimeIndex. Okazuje się, że nie jest to konieczne.To jest szybsze po prostu użyć 'data ['Date'] wartości', aby wyodrębnić podstawową tablicę NumPy z serii Pandas. – unutbu

+0

To jest bardzo pomocne, nie byłem świadomy to. Wierzę, że ta mała sztuczka rozwiąże wiele moich problemów z wykreślaniem DataFrames z Matplotlib. – chilliq

3

natknąłem ten problem po aktualizacji do Pandy 0.21. Mój kod dobrze działał wcześniej z fill_between(), ale zepsuł się po aktualizacji.

Okazuje się, że ta poprawka wspomniano w odpowiedzi @unutbu „s, co jest, co miałem przed każdym razie, działa tylko wtedy, gdy DatetimeIndex zawiera date obiektów zamiast datetime obiektów, które posiada informacje o czasową.

Patrząc na powyższym przykładzie, co zrobiłem było naprawić, aby dodać następującą linię przed wywołaniem fill_between():

d['Date'] = [z.date() for z in d['Date']] 
2

Jak WillZ wskazał, Pandy 0,21 złamał unutbu za obejście. Konwersja datetime na daty może jednak mieć znaczący negatywny wpływ na analizę danych.To rozwiązanie obecnie pracuje i utrzymuje datetime:

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

N = 300 
dates = pd.date_range('2000-1-1', periods=N, freq='ms') 
x = np.linspace(0, 2*np.pi, N) 
data = pd.DataFrame({'A': np.sin(x), 'B': np.cos(x), 
      'Date': dates}) 
d = data['Date'].dt.to_pydatetime() 
plt.plot_date(d, data['A'], '-') 
plt.plot_date(d, data['B'], '-') 


plt.fill_between(d, data['A'], data['B'], 
      where=data['A'] >= data['B'], 
      facecolor='green', alpha=0.2, interpolate=True) 
plt.xticks(rotation=25) 
plt.show() 

fill_between with datetime64 constraint

EDIT: Zgodnie z komentarzem Jedi, wyruszyłem do określania najszybszej podejście z trzech poniższych opcji:

  • method1 = oryginał odpowiedź
  • method2 = komentarz jedi + oryginalna odpowiedź
  • method3 = komentarz jedi

method2 był nieco szybszy, ale o wiele bardziej spójny, dlatego też zredagowałem powyższą odpowiedź, aby odzwierciedlić najlepsze podejście.

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


N = 300 
dates = pd.date_range('2000-1-1', periods=N, freq='ms') 
x = np.linspace(0, 2*np.pi, N) 
data = pd.DataFrame({'A': np.sin(x), 'B': np.cos(x), 
      'Date': dates}) 
time_data = pd.DataFrame(columns=['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']) 
method1 = [] 
method2 = [] 
method3 = [] 
for i in range(0, 10): 
    start = time.clock() 
    for i in range(0, 500): 
     d = [pd.Timestamp(x).to_pydatetime() for x in data['Date']] 
     #d = data['Date'].dt.to_pydatetime() 
     plt.plot_date(d, data['A'], '-') 
     plt.plot_date(d, data['B'], '-') 


     plt.fill_between(d, data['A'], data['B'], 
      where=data['A'] >= data['B'], 
      facecolor='green', alpha=0.2, interpolate=True) 
     plt.xticks(rotation=25) 
     plt.gcf().clear() 
    method1.append(time.clock() - start) 

for i in range(0, 10): 
    start = time.clock() 
    for i in range(0, 500): 
     #d = [pd.Timestamp(x).to_pydatetime() for x in data['Date']] 
     d = data['Date'].dt.to_pydatetime() 
     plt.plot_date(d, data['A'], '-') 
     plt.plot_date(d, data['B'], '-') 


     plt.fill_between(d, data['A'], data['B'], 
      where=data['A'] >= data['B'], 
      facecolor='green', alpha=0.2, interpolate=True) 
     plt.xticks(rotation=25) 
     plt.gcf().clear() 
    method2.append(time.clock() - start) 

for i in range(0, 10): 
    start = time.clock() 
    for i in range(0, 500): 
     #d = [pd.Timestamp(x).to_pydatetime() for x in data['Date']] 
     #d = data['Date'].dt.to_pydatetime() 
     plt.plot_date(data['Date'].dt.to_pydatetime(), data['A'], '-') 
     plt.plot_date(data['Date'].dt.to_pydatetime(), data['B'], '-') 


     plt.fill_between(data['Date'].dt.to_pydatetime(), data['A'], data['B'], 
      where=data['A'] >= data['B'], 
      facecolor='green', alpha=0.2, interpolate=True) 
     plt.xticks(rotation=25) 
     plt.gcf().clear() 
    method3.append(time.clock() - start) 

time_data.loc['method1'] = method1 
time_data.loc['method2'] = method2 
time_data.loc['method3'] = method3 
print(time_data) 
plt.errorbar(time_data.index, time_data.mean(axis=1), yerr=time_data.std(axis=1)) 

time test of 3 methods on converting time data for plotting a DataFrame

+0

To jest szybsze i prostsze obejście 'plt.fill_between (data ['Date']. Dt.to_pydatetime(), ...' To również działa z 'axes.fill_between'. – jedi

+0

@jedi Dziękujemy za polecenie. Edytowałem odpowiedź w odpowiedzi na Twój komentarz. – TurnipEntropy

Powiązane problemy