Matplotlib pandas Квартальная гистограмма с датой и временем как индекс не работает - PullRequest
0 голосов
/ 12 января 2020

У меня есть серия pandas с индексом datetime , которую я пытаюсь визуализировать с помощью гистограммы. Мой код ниже. Но график, который я получаю, не совсем точен (кажется, пи c ниже). Как я могу это исправить? bar chart

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

np.random.seed(100)
dti = pd.date_range('2012-12-31', periods=30, freq='Q')
s2 = pd.Series(np.random.randint(100,1000,size=(30)),index=dti)
df4 = s2.to_frame(name='count')
print('\ndf4:')
print(df4)
print(type(df4))
f2 = plt.figure("Quarterly",figsize=(10,5))
ax = plt.subplot(1,1,1)
ax.bar(df4.index,df4['count'])
plt.tight_layout()
plt.show()

Ответы [ 2 ]

1 голос
/ 13 января 2020

К сожалению, столбчатые графики matplotlib, кажется, не очень хорошо сочетаются с pandas датами.

Теоретически, matplotlib выражает ширину столбцов в днях. Но если вы попробуете что-то вроде ax.bar(df4.index,df4['count'], width=30), вы увидите сюжет с чрезвычайно широкими полосами, почти полностью заполняющий сюжет. Экспериментируя с width, происходит нечто странное. Когда width меньше 2, это выглядит так, будто оно выражено в днях. Но с width больше, чем 2, он внезапно переходит к чему-то гораздо более широкому.

В моей системе (matplotlib 3.1.2, pandas 0.25.3, Windows) это выглядит так: default plot

Обходной путь использует гистограммы от pandas. Похоже, что они делают бары категоричными, с одним тиком на такт. Но они помечены полной датой, включая часы, минуты и секунды. Вы можете перемаркировать их, например, как:

df4.plot.bar(y='count', width=0.9, ax=ax)
plt.xticks(range(len(df4.index)),
           [t.to_pydatetime().strftime("%b '%y") for t in df4.index],
           rotation=90)

Дальнейшее расследование, непоследовательное прыгание вокруг ширины бара matplotlib, похоже, связано со сборкой frequency в pandas раз. Таким образом, решением может быть преобразование дат в даты matplotlib. Если попытаться это сделать, да, значения ширины будут последовательно выражаться в днях.

К сожалению, квартальные даты не имеют одинаковое количество дней между ними, в результате чего некоторые столбцы становятся слишком широкими, а другие - слишком узкими. Решением этой следующей проблемы является явное вычисление количества дней для каждого бара. Чтобы получить хорошее разделение между столбиками, их края должны быть белыми.

from datetime import datetime

x = [datetime.date(t) for t in df4.index]  # convert the pandas datetime to matplotlib's
widths = [t1-t0 for t0, t1 in zip(x, x[1:])]  # time differences between dates
widths += [widths[-1]] # the very last bar didn't get a width, just repeat the last width
ax.bar(x, df4['count'], width=widths, edgecolor='white')

resulting plot

0 голосов
/ 13 января 2020

Вы можете установить ширину столбцов с помощью аргумента width в ax.bar() на некоторое значение, превышающее значение по умолчанию 0.8

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

np.random.seed(100)
dti = pd.date_range('2012-12-31', periods=30, freq='Q')
s2 = pd.Series(np.random.randint(100,1000,size=(30)),index=dti)
df4 = s2.to_frame(name='count')
f2 = plt.figure("Quarterly",figsize=(10,5))
ax = plt.subplot(1,1,1)
ax.bar(df4.index,df4['count'], width=70)
plt.tight_layout()
plt.show()

enter image description here

В этом случае ширина интерпретируется как скаляр в днях.


Редактировать

По некоторым причинам вышеприведенное работает корректно только для старые версии matplotlib (протестировано 2.2.3). Для работы с текущей версией (3.1.2) необходимо внести следующие изменения:

# ...
dti = pd.date_range('2012-12-31', periods=30, freq='Q')
dti = [pd.to_datetime(t) for t in dti]
# ...

, что затем даст правильное поведение при настройке ширины стержней.

...