简体   繁体   中英

Matplotlib - How to draw a line from the top of any bar to the y - axis?

I created a cumulative histogram. Now I want to draw a line from top of any bin to the y-axis in that histogram and show the value of it like this:

Can you show me the way to do? Below is my code to draw that histogram:

plt.rcParams['ytick.right'] = plt.rcParams['ytick.labelright'] = True
plt.rcParams['ytick.left'] = plt.rcParams['ytick.labelleft'] = False

plt.figure(figsize=[8, 6])
plt.hist(df['days'], bins=range(0, 50, 1), color="dodgerblue", edgecolor='black'
                       ,cumulative=-1, density=True
                       ,histtype='barstacked')
plt.xlabel('Number of Days')
plt.ylabel('Density')

Thank you so much!

Oneliner:

plt.axhline(y, color='k', linestyle='dashed', linewidth=1)

Use this to add a horizontal line to your histogram.

Place your mean or value of y in place of y in the above code snippet.

Simply drawing a horizontal line rises two problems:

  • The line will be drawn on top of the bars, from the left to the right. To have it behind the bars, use zorder=0 .
  • The line will still be visible at the far left, as there are no bars there. Changing the x-axis to a "tight" layout with plt.autoscale(enable=True, axis='x', tight=True) solves that.

To add a new tick at the specific y-position, you can take the list of existing ticks, create a list including the new tick and set those as the new ticks.

To change the color of the newly added tick, you first find its index in the list, and then change the color of the tick with that index.

One problem with this approach, is that the new tick might overlap with an existing tick. This could be solved by looping through the list and if an existing tick is nearer than some epsilon to the new tick, remove the existing tick. This is not yet implemented in the code example.

Alternatively, the tick value could be displayed to the left of the axis, on top of the horizontal line. Of course, that would lead to a problem in case there wouldn't be enough place for the text.

You might want to round the value of the special tick to the nearest hundredths to prevent that the other ticks also get displayed with more digits.

I created an example with simulated data:

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

df = pd.DataFrame({"days": np.random.normal(25, 10, 10000)})

plt.rcParams['ytick.right'] = plt.rcParams['ytick.labelright'] = True
plt.rcParams['ytick.left'] = plt.rcParams['ytick.labelleft'] = False

plt.figure(figsize=[8, 6])
bin_heights, _, _ = plt.hist(df['days'], bins=range(0, 50, 1), color="dodgerblue", edgecolor='black',
                             cumulative=-1, density=True,
                             histtype='barstacked')
plt.autoscale(enable=True, axis='both', tight=True)  # use axis='x' to only set the x axis tight
special_y = bin_heights[15]
# draw a horizontal line, use zorder=0 so it is drawn behind the bars
plt.axhline(special_y, 0, 1, color='red', linestyle='dashed', linewidth=1, zorder=0)

plt.yticks(list(plt.yticks()[0]) + [special_y])  # add a tick in y for special_y
# find the index of special_y in the new ticks (ticks are sorted automatically)
index_special_y = list(plt.yticks()[0]).index(special_y)
plt.gca().get_yticklabels()[index_special_y].set_color('red')  # change the color of the special tick
plt.xlabel('Number of Days')
plt.ylabel('Density')
plt.show()

示例图

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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