繁体   English   中英

如何在 matplotlib.axes.Axes.stem 图中显示时间线?

[英]How to show timeline in matplotlib.axes.Axes.stem plot?

我正在做一个matplotlib.axes.Axes.stem图,其中 x 轴是显示天的日期变更线。 我的一些数据出现在特定日期。 而在其他日子里,它没有数据(因为我的数据中不存在此类信息)。

问题 1:如何制作时间轴茎图来显示我的数据,包括没有数据的天数? 这可能吗? 有没有办法自动缩放数据 x 轴的外观来处理这种情况?

下面是一个名为test.txt的示例数据文件和我的 python 脚本,用于读取其数据以显示时间线干图供您考虑。 该脚本的输出也在下面给出。

问题2。 演示问题。 如何在每个注释处显示“-”符号? 另外,如何将注释旋转 30 度?

测试.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

茎图.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()

电流输出:

测试.png

关于你的第一个问题。 基本上,您可以列出您使用和使用的日子之间的所有日子。 因此,将其添加到代码的开头:

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 

这个改变会给你这个:

茎图 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. 添加了旋转参数。 旋转参数没有直接出现在文档中,但文档说您可以使用 Text 作为kwargs任何方法,它们在这里

这有望为您提供您所要求的:

干图 2

添加到@SinanKurmus 对我的第一个问题的回答:

解决方案1:

可以使用 matplotlib 的方法(即drangenum2date以及 python)获得具有给定数据整个历史记录的每日间隔的时间轴。 在这里可以避免使用熊猫。

首先,将时间轴的开始和结束日期表示为python datetime 对象。 请注意,您需要在结束日期上再添加 1 天,否则将不包括上一个日期的数据。 接下来,使用 python 的datetime.timedelta对象使用 1 天作为时间间隔。 接下来将它们提供给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 也有关于如何跳过没有数据的日期的常见问题解答。 我在下面包含了他们的示例代码示例。

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() 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM