0

I have data in pandas DataFrame:

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

np.random.seed(786)

df = pd.DataFrame({'a':np.arange(0, 1, 0.05),
                   'b':np.random.rand(20) - .5})

print (df)
       a         b
0   0.00  0.256682
1   0.05 -0.192555
2   0.10  0.393919
3   0.15 -0.113310
4   0.20  0.373855
5   0.25 -0.423764
6   0.30 -0.123428
7   0.35 -0.173446
8   0.40  0.440818
9   0.45 -0.016878
10  0.50  0.055467
11  0.55 -0.165294
12  0.60 -0.216684
13  0.65  0.011099
14  0.70  0.059425
15  0.75  0.145865
16  0.80 -0.019171
17  0.85  0.116984
18  0.90 -0.051583
19  0.95 -0.096527

I would like plot barplot and add vertical line:

plt.figure(figsize=(10,5))
sns.barplot(x = 'a', y = 'b', data = df)
plt.vlines(x = 0.45, ymin = 0, ymax = 0.6, color = 'red', linewidth=5)

There are problems with ticklabels, because overlaping and also line should be in point 0.45 instaed near 0 for x axis.

polt

I try many solutions from link1, link2, link3, link4 but still problem set correctly axis for both plots.

What is problem? Is possible share x axis between plots?

Expected output - correctly aligned vertical line and also not overlaping ticks in x axis:

pic

jezrael
  • 822,522
  • 95
  • 1,334
  • 1,252
  • working on it! do i understand correctly that you want to share x axis between a plot and a vertical line? – Cut7er Aug 28 '18 at 13:10
  • @Cut7er - Yes, because need correct align vertical line. – jezrael Aug 28 '18 at 13:10
  • then i guess my answer is not helpful since it plots two seperate subplots. but if you could put your line somehow in the figure/subplot construct, it should be possible to just share an axis by "sharex = ax1" – Cut7er Aug 28 '18 at 13:12
  • @Cut7er - I try it, but cannot create working solution :( – jezrael Aug 28 '18 at 13:12
  • have you tried `ax.twiny()`? what is the desired output? – Yuca Aug 28 '18 at 13:17
  • @Yuca - Please check edited question with expected output. – jezrael Aug 28 '18 at 13:21
  • no overlapping can be achieved with rotating labels, do you agree? – Yuca Aug 28 '18 at 13:23
  • @Yuca - hmmm, I try `plt.figure(figsize=(28,10))` and problem seems with `floats` - e.g `0.15` is changed to `0.1500000000002`. So not sure. – jezrael Aug 28 '18 at 13:25
  • if that is the case ax.set_xticklabels(np.round()) should fix it – Yuca Aug 28 '18 at 13:27
  • @Yuca - Be free create answer ;) But hardest problem seems with vertical line :( – jezrael Aug 28 '18 at 13:28
  • since I consider you one of the python masters I didn't want to answer since I feel underqualified, so I gave pointers, I'll give you an answer in an hour, I'm in the middle of something :) – Yuca Aug 28 '18 at 13:38
  • 1
    I played around with the vertical line and `plt.vlines(x = 0.45*20, ...)` seems to work. But that is very strange! Might be a bug between seaborn and matplotlib interface. – Parfait Aug 28 '18 at 14:24

3 Answers3

4

The x-axis in the barplot is categorical, so it doesn't have the values of your df.a as a real scale, but only as tick labels. You could change e.g. df.a[19] = 2 and nothing will change except the label of the last bar tick.

So categorical axis means the coordinates are 0 for the first bar, 1 for the second and so on ... 19 for the last.

My approach then would be to set the vertical line at xpos * 19/.95:

plt.vlines(x = .45*19/.95, ymin = 0, ymax = 0.6, color = 'red', linewidth=5)

enter image description here

For the general case you could add a lambda function to calculate the conversion:

f = lambda x: (x-df.a.values[0]) * (df.a.size-1) / (df.a.values[-1] - df.a.values[0])
plt.vlines(x = f(.45), ymin = 0, ymax = 0.6, color = 'red', linewidth=5)

However, as df.a.values is only printed as tick labels, it should go linearly from start to end.

Regarding the problem with x-axis labeling: I just can tell that it doesn't appear at my system, the code for the plot abovevis identical to yours, except the vertical line. Perhaps it was introduced while doing one attempt of vlines after another.

SpghttCd
  • 10,510
  • 2
  • 20
  • 25
  • Can you add picture of plot to answer? – jezrael Aug 28 '18 at 14:22
  • Consider also adding full graphing code to show how x-axis was resolved. – Parfait Aug 29 '18 at 17:13
  • Sorry, I thought it's obvious by the plot: the graphing code is the identical copy of the one posted in the question - except the last line which plots the vertical line. This one is replaced by the described version. In the general case additionally by the lambda definition. – SpghttCd Aug 29 '18 at 18:28
  • You are completely right - I should have adressed the x-axis issue explicitely, as it was part of the question. I'll edit... – SpghttCd Aug 29 '18 at 18:37
1

Using ax.twiny and rounding inputs:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
np.random.seed(786)

df = pd.DataFrame({'a':np.round(np.arange(0, 1, 0.05),2),
                   'b':np.round(np.random.rand(20),2) - .5})


plt.figure(figsize=(10,5))
ax = sns.barplot(x = 'a', y = 'b', data = df)
ax.set_xticklabels(ax.get_xticklabels(), rotation=90)
ax2 = ax.twiny()
ax2.vlines(x = 0.45, ymin = 0, ymax = 0.6, color = 'red', linewidth=2)
#ax2.set_visible(False) # this hides the ticks on the top of the plot

enter image description here

Yuca
  • 6,010
  • 3
  • 22
  • 42
0

create a figure with two subplots, then you can share x- and y axis between both subplots.

fig = plt.figure()
ax1 = plt.subplot(211)
ax2 = plt.subplot(212, sharex = ax1, sharey = ax1)
Cut7er
  • 1,209
  • 9
  • 24