I have dataframe with multi-index (sorted by "5min_intervals" and "price" indexes).
quantity
5min_intervals price
2023-07-27 17:40:00 172.20 330
172.19 1
2023-07-27 17:45:00 172.25 4
172.24 59
172.23 101
172.22 224
172.21 64
172.20 303
172.19 740
172.18 26
2023-07-27 17:50:00 172.17 30
172.16 2
172.15 1014
172.14 781
172.13 1285
I know about simple horizontal bar chart with df.plot.barh()
. Also I can iterate dataframe by '5min_intervals' index with
for date in df.index.levels[0]:
print(df.loc[date])
and get dataframes for each '5min_intervals' index like below
quantity
price
172.20 330
172.19 1
Is there any way to create one chart with matplotlib where for each '5min_intervals' index would be horizontal bar chart. Schematically it looks like in below picture
Let's prepare some dummy data to work with:
from pandas import DataFrame, Timestamp
data = {
'quantity': {
(Timestamp('2023-07-27 17:40:00'), 172.2): 330,
(Timestamp('2023-07-27 17:40:00'), 172.19): 1,
(Timestamp('2023-07-27 17:45:00'), 172.25): 4,
(Timestamp('2023-07-27 17:45:00'), 172.24): 59,
(Timestamp('2023-07-27 17:45:00'), 172.23): 101,
(Timestamp('2023-07-27 17:45:00'), 172.22): 224,
(Timestamp('2023-07-27 17:45:00'), 172.21): 64,
(Timestamp('2023-07-27 17:45:00'), 172.2): 303,
(Timestamp('2023-07-27 17:45:00'), 172.19): 740,
(Timestamp('2023-07-27 17:45:00'), 172.18): 26,
(Timestamp('2023-07-27 17:50:00'), 172.17): 30,
(Timestamp('2023-07-27 17:50:00'), 172.16): 2,
(Timestamp('2023-07-27 17:50:00'), 172.15): 1014,
(Timestamp('2023-07-27 17:50:00'), 172.14): 781,
(Timestamp('2023-07-27 17:50:00'), 172.13): 1285
}
}
df = DataFrame(data)
As for the graphics, there's hardly anything like you asked ready from the box. So we need to build it manually. At first, let's do some basic preparation:
unique_time = df.index.get_level_values(0).unique().sort_values()
unique_price = df.index.get_level_values(1).unique().sort_values()
spacing = 0.2 # a minimum distance between two consecutive horizontal lines
values = (1-spacing) * df/df.max() # relative lengths of horizontal lines
base = DataFrame(index=unique_price) # the widest blank frame with prices
To build the graphics, we can use barh(y, width, height, left)
- horizontal bar - with prices as the first parameter, time as left shifting, values in place of width, and some fixed small height. To make very small values visible, we can additionally mark the beginning (left end) of a line with a small tick using barh
again.
import matplotlib.pyplot as plt
from matplotlib.colors import TABLEAU_COLORS
from itertools import cycle
fig, ax = plt.subplots(figsize=(7,7))
xlabels = unique_time.astype(str)
ylabels = unique_price.astype(str)
ax.set_xticks(range(len(xlabels)), xlabels, rotation=45)
ax.set_yticks(range(len(ylabels)), ylabels)
ax.set_xlim([-0.5,len(xlabels)])
ax.set_ylim([-1, len(ylabels)])
for i, (t, c) in enumerate(zip(unique_time, cycle(TABLEAU_COLORS))):
pr = base.join(values.loc[t]).squeeze()
# draw horizontal lines, shifted left by i-th timepoint
ax.barh(ylabels, pr, 0.1, i, color=c)
# put tics at the left end of lines, i.e. their beginning
ax.barh(ylabels, 0.01*pr.notna(), 0.3, i, color=c)
ax.grid(axis='y', linestyle='--', linewidth=0.5)
fig.tight_layout()
plt.show()
python : 3.11
pandas : 1.5.1
matplotlib : 3.6.1