Pandas и Matplotlib - fill_between() vs datetime64
Существует Pandas DataFrame:
<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
Я хочу построить серию A и B vs Date.
plt.plot_date(data['Date'], data['A'], '-')
plt.plot_date(data['Date'], data['B'], '-')
Затем я хочу применить fill_between() в области между сериями A и B:
plt.fill_between(data['Date'], data['A'], data['B'],
where=data['A'] >= data['B'],
facecolor='green', alpha=0.2, interpolate=True)
Какие выходы:
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''
Принимает ли matplotlib объект Pandas datetime64 в функции fill_between()
? Должен ли я преобразовать его в другой тип даты?
Ответы
Ответ 1
Pandas регистрирует конвертер в matplotlib.units.registry
, который преобразует количество типов datetime (таких как pandas DatetimeIndex и массивы numpy dtype datetime64
) к датам данных matplotlib, но он не обрабатывает pandas Series
с 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
проверяет и использует конвертер для обработки данных, если он существует.
Итак, в качестве обходного пути вы можете преобразовать даты в массив NumPy из datetime64
's:
d = data['Date'].values
plt.fill_between(d, data['A'], data['B'],
where=data['A'] >= data['B'],
facecolor='green', alpha=0.2, interpolate=True)
Например,
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]()
Ответ 2
Я столкнулся с этой проблемой после обновления до Pandas 0.21. Ранее мой код работал с fill_between()
, но после обновления.
Оказывается, это исправление, упомянутое в ответе @unutbu, которое было тем, что у меня было до сих пор, работает только в том случае, если DatetimeIndex
содержит объекты date
, а не объекты datetime
, которые имеют информацию о времени.
Глядя на пример выше, я попытался добавить следующую строку перед вызовом fill_between()
:
d['Date'] = [z.date() for z in d['Date']]
Ответ 3
Как заметил WillZ, Pandas 0,21 нарушил обходное решение unutbu. Однако преобразование дат к датам может оказать значительное негативное влияние на анализ данных. Это решение в настоящее время работает и поддерживает 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 с ограничением datetime64]()
ИЗМЕНИТЬ: В соответствии с комментарием jedi, я решил определить самый быстрый из трех вариантов ниже:
- method1 = оригинальный ответ
- method2 = комментарий jedi + оригинальный ответ
- method3 = комментарий jedi
method2 был немного быстрее, но гораздо более последовательным, и поэтому я отредактировал вышеупомянутый ответ, чтобы отразить наилучший подход.
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))
![тест времени 3 методов преобразования данных времени для построения DataFrame]()