Matplotlib

From Torben's Wiki

Matplotlib

Plotting using pandas and matplotlib

plot time series data with fancy formatting using ConciseDateFormatter, german locale, etc.

import matplotlib.pyplot as plt
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import matplotlib.dates as mdates
import locale
import pandas as pd
locale.setlocale(locale.LC_ALL, "de_DE.UTF-8")

...

fig, ax = plt.subplots(
    nrows=1,
    ncols=1,
    # figsize=(6, 8),  # default = 6.4,4.8
    dpi=100,
)
# , sharex=True,

# plot data
df["value_pct"].plot(ax=ax, style=".", legend=True, zorder=2)
df["linregress"].plot(ax=ax, style="-", legend=True, zorder=1)
plt.legend(["Data", "Fit"])

# plot 2 guiding lines
y_last = df["value_pct"].iloc[-1]
# y_min = df["value_pct"].min()
# y_max = df["value_pct"].max()
plt.axhline(y=0.0, color="black", linestyle="-")
plt.axhline(y=y_last, color="blue", linestyle="dotted")

# x tics
# fancy formatting using ConciseDateFormatter
x_tic_locator = mdates.AutoDateLocator(minticks=3, maxticks=7)
x_tic_formatter = mdates.ConciseDateFormatter(
    x_tic_locator,
    show_offset=True,
    offset_formats=["", "%Y", "%b %Y", "%Y-%b-%d", "%Y-%b-%d", "%Y-%b-%d %H:%M"],
)
# ax.xaxis.set_major_locator(x_tic_locator)
ax.xaxis.set_major_formatter(x_tic_formatter)

# y tics
ax.yaxis.set_major_locator(mticker.MaxNLocator(integer=True))
ax.yaxis.set_major_formatter(mticker.PercentFormatter(decimals=0))

# title
plt.suptitle("my Title")
# ax.set_title("my Subtitle") # "" to remove
plt.xlabel("xLabel") # "" to remove
plt.ylabel("yLabel") # "" to remove

# layout
plt.grid(axis="both")
plt.tight_layout()

# save and close
plt.savefig(fname=f"plot/{isin}-{date_selection}.png", format="png")
# plt.clf()
plt.close() # prevents figure.max_open_warning


horizontal bar chart of rows "plant" and "cnt"

myPlot = df.plot.barh(legend=False, x='plant', y='cnt', linewidth=2.0, zorder=1, figsize=(12, 12))
plt.gca().invert_yaxis()
plt.gca().xaxis.set_major_formatter(mticker.PercentFormatter()) #
myPlot.set_ylim(0, 100)
plt.title('My Title')
plt.xlabel("")
# x y grid
plt.gca().set_axisbelow(True)  # for grid below the lines
plt.grid(axis='both')
# x grid for bar chart
plt.grid(axis='x')
plt.tight_layout()
plt.savefig(fname='out.png', format='png')

2 Subplots sharing xaxis

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

# initialize plot
fig, axes = plt.subplots(nrows=2, ncols=1, sharex=True, figsize=(6, 8)  # default = 6.4,4.8
            ,dpi=100)
fig.suptitle(f"COVID-19 in {long_name}")  # super title
axes[0].set_title("Inzidenzwert und -anstieg", fontsize=10)
axes[1].set_title("Tote und Intensivstationsbelegung", fontsize=10)

# define colors for data
colors = (('blue', 'red'), ('purple', 'green'))

# plot the data of a pandas dataframe
df['Inzidenz'].plot(
    ax=axes[0], color=colors[0][0], legend=False, secondary_y=False, zorder=2, linewidth=2.0)
df['Inzidenzanstieg'].plot.area(
    ax=axes[0], color=colors[0][1], legend=False, secondary_y=True, zorder=1)
df['Tote'].plot(
    ax=axes[1], color=colors[1][0], legend=False, secondary_y=False, zorder=2, linewidth=2.0)
df['Intensivstationsbelegung'].plot.area(
    ax=axes[1], color=colors[1][1], legend=False, secondary_y=True, zorder=1, linewidth=2.0) 

# Axis layout, text and range
# remove label as date is obvious
axes[1].set_xlabel("")

# top plot
axes[0].set_title("Inzidenzwert und -anstieg", fontsize=10)
axes[1].set_title("Tote und Intensivstationsbelegung", fontsize=10)
# axis label
axes[0].set_ylabel('Inzidenz (7 Tage)')
axes[0].right_ax.set_ylabel('Inzidenzanstieg (7 Tage)')
axes[1].set_ylabel('Tote (7 Tage pro Millionen)')
axes[1].right_ax.set_ylabel('Intensivstationen Anteil COVID-Patienten')
# axis range
axes[0].set_ylim(0, )  # 0,550
axes[0].right_ax.set_ylim(0, 150)
axes[1].set_ylim(0, )  # 0,250
axes[1].right_ax.set_ylim(0, 40)
# tick freq
# all are set to make charts better compareable
axes[0].yaxis.set_major_locator(ticker.MultipleLocator(50))
axes[0].right_ax.yaxis.set_major_locator(ticker.MultipleLocator(25))
axes[1].yaxis.set_major_locator(ticker.MultipleLocator(25))
axes[1].right_ax.yaxis.set_major_locator(ticker.MultipleLocator(10))
# tick format
axes[0].yaxis.set_major_formatter(ticker.FormatStrFormatter('%d'))
axes[0].right_ax.yaxis.set_major_formatter(
 ticker.PercentFormatter(decimals=0))
axes[1].yaxis.set_major_formatter(ticker.FormatStrFormatter('%d'))
axes[1].right_ax.yaxis.set_major_formatter(
 ticker.PercentFormatter(decimals=0))
# color of label and ticks
axes[0].yaxis.label.set_color(colors[0][0])
axes[0].tick_params(axis='y', colors=colors[0][0])
axes[0].right_ax.yaxis.label.set_color(colors[0][1])
axes[0].right_ax.tick_params(axis='y', colors=colors[0][1])
axes[1].yaxis.label.set_color(colors[1][0])
axes[1].tick_params(axis='y', colors=colors[1][0])
axes[1].right_ax.yaxis.label.set_color(colors[1][1])
axes[1].right_ax.tick_params(axis='y', colors=colors[1][1])
# zorder problem
# 1. per axis
# 2. per series in axis including grid
# Problem: can't solve the problem, that data of the secondary y axis is plotted below the grid of the 1st axis
axes[0].grid(zorder=-1)
axes[0].set_zorder(axes[0].right_ax.get_zorder()+1)
axes[0].patch.set_visible(False)
axes[1].grid(zorder=-1)
axes[1].set_zorder(axes[1].right_ax.get_zorder()+1)
axes[1].patch.set_visible(False)

Heatmap / Colormap

import pandas as pd
# import seaborn as sns
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

# plot heatmap
# V1
# ax = sns.heatmap(df.T)
# plt.savefig("data.png", dpi=100)

# V2
dates = df.index.to_pydatetime()
dnum = mdates.date2num(dates)
start = dnum[0] - (dnum[1] - dnum[0]) / 2.0
stop = dnum[-1] + (dnum[1] - dnum[0]) / 2.0
extent = [start, stop, -0.5, len(df.columns) - 0.5]

fig, ax = plt.subplots(figsize=(9, 16), zorder=1)
# im = ax.imshow(df.T.values, extent=extent, aspect="auto")
im = ax.imshow(
    df.T.values * (-1),
    cmap="jet",
    interpolation="none",
    origin="lower",
    aspect="auto",
    extent=extent,
)


ax.set_yticks(range(0, len(list(df.columns))))
ax.set_yticklabels(df.columns)

ax.xaxis.set_major_locator(mdates.DayLocator())
# # ax.xaxis.set_minor_locator(mdates.DayLocator())
ax.xaxis.set_major_formatter(mdates.DateFormatter("%d.%m."))

# fig.colorbar(im)

# plt.gca().set_axisbelow(True)  # for grid below the lines
# plt.grid(axis="both", zorder=-1)
plt.tight_layout()
ax.margins(y=0.9, x=0.9)

plt.savefig("data-heatmap.png", dpi=100)

Defaults

Default Colors

import matplotlib.colors as mcolors

print("mcolors.TABLEAU_COLORS")
for name, value in mcolors.TABLEAU_COLORS.items():
    print(name, "\t", value)

colors = mcolors.TABLEAU_COLORS.keys()
print(colors)

tab:blue         #1f77b4
tab:orange       #ff7f0e
tab:green        #2ca02c
tab:red          #d62728
tab:purple       #9467bd
tab:brown        #8c564b
tab:pink         #e377c2
tab:gray         #7f7f7f
tab:olive        #bcbd22
tab:cyan         #17becf

more stuff

# using 2nd axis and filled area
ax1 = df.Cases_Last_Week_Per_100000.plot(
      color="blue", legend=False, secondary_y=False, zorder=2)
ax1.set_zorder(2)
# important: transparent background for line plot
ax1.set_facecolor('none')
ax2 = df.Cases_Last_Week_7Day_Percent.plot.area(color="red",
                                                   legend=False, secondary_y=True, zorder=1)
ax2.set_zorder(1)

# set axis range
ax1.set_ylim(0, )
ax2.set_ylim(0, 200)

# set axis label
ax1.set_ylabel('Inzidenz (7 Tage)')
ax2.set_ylabel('Anstieg (7 Tage)')
# no label for x axis
# plt.xlabel("")
ax1.set_xlabel("")
ax2.set_xlabel("")

# tics as percentage
import matplotlib.ticker as mtick
ax2.yaxis.set_major_formatter(mtick.PercentFormatter())

# axis numbers and label colors 
ax1.yaxis.label.set_color('blue')
ax1.tick_params(axis='y', colors='blue')
ax2.yaxis.label.set_color('red')
ax2.tick_params(axis='y', colors='red')

# set locale language setting for date axis etc.
import locale
locale.setlocale(locale.LC_ALL, 'de_DE.UTF-8')

# add text to bottom right
plt.gcf().text(1.0, 0.0, s="by Torben https://entorb.net , based on RKI and DIVI data", fontsize=8,
               horizontalalignment='right', verticalalignment='bottom', rotation='vertical')