Matplotlib: Как я могу добавить чередующийся цвет фона, когда у меня есть даты на оси X? - PullRequest
0 голосов
/ 12 февраля 2019

Я недавно начал использовать тему dark chesterish из dunovank , и мне нравится, как хорошо выглядит простой pandas.DataFrame.plot ()как из коробки:

Фрагмент 1 :

# Theme from dunovank, exclude if not installed:
from jupyterthemes import jtplot
jtplot.style()

# snippet from pandas docs:
ts = pd.Series(np.random.randn(1000),index=pd.date_range('1/1/2000', periods=1000)).cumsum()
ax = ts.plot()

Выход 1:

enter image description here

Но я бы хотел добавить чередующийся фоновый цвет (кажется, все в моде с крупными информационными агентствами).Пост Как мне установить цвет фона для определенных областей рисунка пиплота? дает хорошее описание того, как вы можете это сделать.И это действительно просто для числовых значений x :

Фрагмент 2 :

# imports
import pandas as pd
import numpy as np
from jupyterthemes import jtplot

# Sample data
np.random.seed(123)
rows = 50
dfx = pd.DataFrame(np.random.randint(90,110,size=(rows, 1)), columns=['Variable Y'])
dfy = pd.DataFrame(np.random.randint(25,68,size=(rows, 1)), columns=['Variable X'])
df = pd.concat([dfx,dfy], axis = 1)
jtplot.style()

ax = df.plot()
for i in range(0, 60, 20):       
            ax.axvspan(i, i+10, facecolor='lightgrey', alpha=0.025)

Вывод2:

enter image description here

Но это становится намного грязнее (по крайней мере для меня), когда ось x имеет формат времени или даты,И это потому, что ось в моих двух примерах идет от этого

# in:
ax.lines[0].get_data()

# out:
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
        17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
        34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
       dtype=int64)

К этому (сокращенно):

# in:
ts.plot().lines[0].get_data()

# out:
.
.
Period('2002-09-15', 'D'), Period('2002-09-16', 'D'),
Period('2002-09-17', 'D'), Period('2002-09-18', 'D'),
Period('2002-09-19', 'D'), Period('2002-09-20', 'D'),
Period('2002-09-21', 'D'), Period('2002-09-22', 'D'),
Period('2002-09-23', 'D'), Period('2002-09-24', 'D'),
Period('2002-09-25', 'D'), Period('2002-09-26', 'D')], dtype=object)  

ts.plot().lines[0].get_data() возвращает данные по оси X.Но есть ли способ узнать, где matplotlib отображает вертикальные линии для каждого наблюдения 'Jan' , чтобы мне было легче найти приличные интервалы для чередующегося черного и серого цвета фона?

enter image description here

Спасибо за любые предложения!


Редактировать - Или есть тема?

Или кто-нибудь знает, существует ли где-нибудь тема, которую можно бесплатно использовать?Я проверил все темы matplotlib import matplotlib.pyplot as plt; print(plt.style.available) и Seaborn , но безуспешно.


Правка 2 - предлагаемое решение от ImportanceOfBeingErnest с активированной темой честериста:

enter image description here

По моему скромному мнению, это идеальная установка для графика временного ряда (хотя, возможно, может быть и сплайны)

Ответы [ 2 ]

0 голосов
/ 12 февраля 2019

Сетки по умолчанию показаны в позициях основных тиков.Вы можете получить эти тики через ax.get_xticks().Проблема будет в том, что не гарантируется, что края графика совпадают с этими галочками, на самом деле они чаще всего отличаются.Таким образом, чтобы иметь равномерное затенение по диапазону осей, первый оттенок должен начинаться на краю графика и заканчиваться на первой линии сетки, затем следующие линии могут переходить между линиями сетки до последней, что будетснова оказаться между последней линией сетки и краем осей.

Другая проблема заключается в том, что пределы графика и, следовательно, автоматически созданные линии сетки могут изменяться в течение срока жизни графика, например, потому что вы решили иметь разныеограничивает или масштабирует или панорамирует сюжет.Таким образом, в идеале можно воссоздавать затенение каждый раз, когда изменяются пределы оси.Вот что делает следующее:

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

# time series
ts = pd.Series(np.random.randn(1000),index=pd.date_range('1/1/2000', periods=1000)).cumsum()
# numeric series
#ts = pd.Series(np.random.randn(1000),index=np.linspace(25,800,1000)).cumsum()
ax = ts.plot(x_compat=True)

ax.grid()

class GridShader():
    def __init__(self, ax, first=True, **kwargs):
        self.spans = []
        self.sf = first
        self.ax = ax
        self.kw = kwargs
        self.ax.autoscale(False, axis="x")
        self.cid = self.ax.callbacks.connect('xlim_changed', self.shade)
        self.shade()
    def clear(self):
        for span in self.spans:
            try:
                span.remove()
            except:
                pass
    def shade(self, evt=None):
        self.clear()
        xticks = self.ax.get_xticks()
        xlim = self.ax.get_xlim()
        xticks = xticks[(xticks > xlim[0]) & (xticks < xlim[-1])]
        locs = np.concatenate(([[xlim[0]], xticks, [xlim[-1]]]))

        start = locs[1-int(self.sf)::2]  
        end = locs[2-int(self.sf)::2]

        for s, e in zip(start, end):
            self.spans.append(self.ax.axvspan(s, e, zorder=0, **self.kw))

gs = GridShader(ax, facecolor="lightgrey", first=False, alpha=0.7)

plt.show()

enter image description here

0 голосов
/ 12 февраля 2019

Используйте вертикальный диапазон оси со значениями даты и времени для значений x:

from jupyterthemes import jtplot
import pandas as pd
import numpy as np
from datetime import datetime

jtplot.style()
ts = pd.Series(np.random.randn(1000),index=pd.date_range('1/1/2000', periods=1000)).cumsum()
ax = ts.plot()

# or an appropriate for-loop
ax.axvspan(datetime(1999, 12, 15), datetime(2000, 1, 15), facecolor='red', alpha=0.25)
ax.axvspan(datetime(2000, 12, 15), datetime(2001, 1, 15), facecolor='red', alpha=0.25)

timeseries graph with shaded vertical areas

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...