Как показать временную шкалу в сюжете matplotlib.axes.Axes.stem? - PullRequest
0 голосов
/ 22 февраля 2020

Я делаю график matplotlib.axes.Axes.stem, где ось X - это линия даты, которая показывает дни. Некоторые из моих данных появляются в определенные дни. В то время как в другие дни у него нет данных (потому что такая информация не существует в моих данных).

Вопрос 1: Как мне составить график стволовой шкалы, который будет показывать мои данные, включая дни без данных? Это возможно? Есть ли способ автоматически масштабировать внешний вид оси X данных, чтобы справиться с такой ситуацией?

Ниже приведен пример файла данных с именем test.txt и моего python сценария, который нужно прочитать в его данных, чтобы показать график стволовой шкалы для вашего рассмотрения. вывод этого скрипта также приведен ниже.

Вопрос 2. Вопрос о презентации. Как мне показать символ «-» в каждой аннотации? Кроме того, как повернуть аннотацию на 30 градусов?

test.txt

No. Date 
1   23/01/2020
2   24/01/2020
3   24/01/2020
4   26/01/2020
5   27/01/2020
6   28/01/2020
7   29/01/2020
8   29/01/2020
9   30/01/2020
10  30/01/2020
11  31/01/2020
12  31/01/2020
13  01/02/2020
14  01/02/2020
15  04/02/2020
16  04/02/2020
17  04/02/2020
18  05/02/2020
19  05/02/2020
20  05/02/2020
21  06/02/2020
22  07/02/2020
23  07/02/2020
24  07/02/2020
25  08/02/2020
26  08/02/2020
27  08/02/2020
28  08/02/2020
29  08/02/2020
30  09/02/2020
31  10/02/2020
32  10/02/2020
33  11/02/2020
34  11/02/2020
38  13/02/2020
39  13/02/2020
40  13/02/2020
41  13/02/2020
42  13/02/2020
43  13/02/2020
44  14/02/2020
45  14/02/2020
46  14/02/2020
47  14/02/2020
48  14/02/2020
49  14/02/2020
50  15/02/2020
51  15/02/2020
52  15/02/2020
53  15/02/2020
54  15/02/2020
57  18/02/2020
58  18/02/2020
59  18/02/2020
60  19/02/2020
61  21/02/2020

stem_plot.py

import matplotlib.pyplot as plt
import numpy as np
import matplotlib.dates as mdates
from datetime import datetime

from pathlib import Path

#########################
#### DATA EXTRACTION ####
#########################
source = Path('./test.txt')
with source.open() as f:
  lines = f.readlines()
#print( lines )

# Store source data in dictionary with date shown as mm-dd. 
data={}
for line in lines[1:]:
    case, cdate = line.strip().split()
    cdate = datetime.strptime(cdate, "%d/%m/%Y").strftime('%m-%d')
    data[case] = cdate
print( f'\ndata = {data}' )

# Collate data's y-axis for each date, i.e. history
history2={}
cdates = list(data.values())
sorted_dates = sorted( set( cdates ) )
for i in sorted_dates:
    cases=[]
    for case, date in data.items():
        if i == date:
            cases.append(case)
    #print( i, cases)
    history2[i] = cases 
print( f'\nhistory2 = {history2}')

###########################
#### DATA PRESENTATION ####
###########################
# Create figure and plot a stem plot with the date
fig, ax = plt.subplots(figsize=(8.8, 5), constrained_layout=True)
ax.set(title="Test")

labels=list( history2.values() ) # For annotation 
yy = [ len(i) for i in labels ]  # y-axis
xx = list(history2.keys())       # x-axis
markerline, stemline, baseline = ax.stem(
    xx, yy, linefmt="C1:", basefmt="k-", use_line_collection=True)

plt.setp(markerline, marker="None" ) 

# annotate stem lines
for ann_x, label in list(history2.items()):
    print(ann_x, label)
    each_count=1
    for each in label:
        ax.annotate( each, xy=(ann_x, each_count), xycoords='data')
        each_count += 1
        #print(f'each_count = {each_count}' )

# format xaxis
plt.setp( ax.get_xticklabels(), rotation=30 )

# remove top and right spines
for spine in ["top", "right"]:
    ax.spines[spine].set_visible(False)

# show axis name
ax.get_yaxis().set_label_text(label='Y-axis')
ax.get_xaxis().set_label_text(label='X-axis')

plt.show()

Токовый выход:

test.png

Ответы [ 2 ]

1 голос
/ 22 февраля 2020

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

import pandas as pd
alldays = pd.date_range(start="20200123", 
                     end="20200221", 
                     normalize=True)
dates = []
for i in alldays:
    dates.append(f"{i.month:02}-{i.day:02}")

Что он делает, он получает диапазон данных pandas между двумя датами и преобразует этот диапазон в список строк месяца-дня.

Затем измените эту часть вашего кода следующим образом:

# Collate data's y-axis for each date, i.e. history
history2={}
cdates = list(data.values())
sorted_dates = sorted( set( cdates ) )
for i in dates:  # This is the only change!
    cases=[]
    for case, date in data.items():
        if i == date:
            cases.append(case)
    #print( i, cases)
    history2[i] = cases 

И это изменение даст вам следующее:

Stem Plot 1

Что касается вашего второго вопроса, измените свой код так:

# annotate stem lines
for ann_x, label in list(history2.items()):
    print(ann_x, label)
    each_count=1
    for each in label:
        ax.annotate(f"--{each}", xy=(ann_x, each_count), xycoords='data', rotation=30)
        each_count += 1

Я только что изменил строку ax.annotate. Два изменения:

  1. добавил «-» к каждой из ваших меток аннотации,
  2. добавил параметр поворота. Параметр поворота не отображается непосредственно в документации , но в документации говорится, что вы можете использовать любой из методов для текста как kwargs, и они здесь .

Мы надеемся, что это даст вам то, о чем вы просили:

Stem Plot 2

0 голосов
/ 24 февраля 2020

Добавление к @SinanKurmus ответа на мой 1-й вопрос:

Решение1:

Временная ось с дневным интервалом для всей истории данных может быть получены с использованием методов matplotlib, а именно drange и num2date и python. Здесь можно избежать использования pandas.

Сначала express начальная и конечная дата оси времени в качестве python объекта даты и времени. Обратите внимание, что вам нужно добавить еще 1 день к дате окончания, иначе данные с последней даты не будут включены. Затем используйте 1 день в качестве временного интервала, используя объект python 'datetime.timedelta. Затем передайте их методу matplotlib.date.drange, который вернет массив NumPy. Метод Matplotlib num2date по очереди преобразует его обратно в python объект datetime.

def get_time_axis( data ):
    start = datetime.strptime(min(data.values()), "%Y-%m-%d")
    end = datetime.strptime(max(data.values()), "%Y-%m-%d") + timedelta(days=1)
    delta = timedelta(days=1)
    time_axis_md = mdates.drange( start, end, delta )
    time_axis_py = mdates.num2date( time_axis_md, tz=None ) # Add tz when required
    return time_axis_py

Решение 2:

Очевидно, у Matplotlib также есть FAQ по , как пропустить даты, когда данных нет . Я включил их пример кода ниже.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
import matplotlib.ticker as ticker

r = mlab.csv2rec('../data/aapl.csv')
r.sort()
r = r[-30:]  # get the last 30 days

N = len(r)
ind = np.arange(N)  # the evenly spaced plot indices

def format_date(x, pos=None):
    thisind = np.clip(int(x+0.5), 0, N-1)
    return r.date[thisind].strftime('%Y-%m-%d')

fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(ind, r.adj_close, 'o-')
ax.xaxis.set_major_formatter(ticker.FuncFormatter(format_date))
fig.autofmt_xdate()

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