This question already has answers here:
multiple axis in matplotlib with different scales [duplicate]
(3 answers)
Closed 5 years ago.
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import seaborn as sns
d = ['d1','d2','d3','d4','d5','d6']
value = [111111, 222222, 333333, 444444, 555555, 666666]
y_cumsum = np.cumsum(value)
sns.barplot(d, value)
sns.pointplot(d, y_cumsum)
plt.show()
I'm trying to make pareto diagram with barplot and pointplot. But I can't print percentages to the right side ytick. By the way, if I manuplate yticks it overlaps itself.
plt.yticks([1,2,3,4,5])
overlaps like in the image.
Edit: I mean that I want to quarter percentages (0, 25%, 50%, 75%, 100%) on the right hand side of the graphic, as well.
From what I understood, you want to show the percentages on the right hand side of your figure. To do that, we can create a second y axis using twinx(). All we need to do then is to set the limits of this second axis appropriately, and set some custom labels:
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
d = ['d1','d2','d3','d4','d5','d6']
value = [111111, 222222, 333333, 444444, 555555, 666666]
fig, ax = plt.subplots()
ax2 = ax.twinx() # create a second y axis
y_cumsum = np.cumsum(value)
sns.barplot(d, value, ax=ax)
sns.pointplot(d, y_cumsum, ax=ax)
y_max = y_cumsum.max() # maximum of the array
# find the percentages of the max y values.
# This will be where the "0%, 25%" labels will be placed
ticks = [0, 0.25*y_max, 0.5*y_max, 0.75*y_max, y_max]
ax2.set_ylim(ax.get_ylim()) # set second y axis to have the same limits as the first y axis
ax2.set_yticks(ticks)
ax2.set_yticklabels(["0%", "25%","50%","75%","100%"]) # set the labels
ax2.grid("off")
plt.show()
This produces the following figure:
Related
This question already has answers here:
How can I change the x axis in matplotlib so there is no white space?
(2 answers)
Closed 3 years ago.
Whenever I plot figures using matplotlib or seaborn, there is always some whitespace remaining at the sides of the plot and the top and bottom of the plot. The (x_0,y_0) is not in the bottom left corner, x_0 is offset a little bit to the right and y_0 is offset a little bit upwards for some reason? I will demonstrate a quick example below with the 'ggplot' style so it is clear what I mean:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.style.use('ggplot')
fig = plt.figure()
x = np.linspace(0,5,11)
ax = fig.add_axes([0.1,0.1,1,1])
ax.plot(x,x**2)
How do I get (0,0) to the bottom left corner and how do I get rid of the unnecessary space where y > 25, x >5?
Thank you.
The "whitespace" is caused by the plot margins. A better way to get rid of them without changing the axes limits explicitly is to set 0-margins
plt.style.use('ggplot')
fig = plt.figure()
x = np.linspace(0,5,11)
ax = fig.add_axes([0.1,0.1,1,1])
ax.margins(x=0,y=0)
ax.plot(x,x**2)
Alternatively:
x = np.linspace(0,5,11)
plt.xlim((0,5))
plt.ylim((0,25))
plt.plot(x,x**2);
To not have borders you can use set_xlim and set_ylim:
ax.set_xlim([0, 5])
ax.set_ylim([0, 25])
Full code:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.style.use('ggplot')
fig = plt.figure()
x = np.linspace(0,5,11)
ax = fig.add_axes([0.1,0.1,1,1])
ax.plot(x,x**2)
ax.set_xlim([0, 5])
ax.set_ylim([0, 25])
plt.show()
So currently learning how to import data and work with it in matplotlib and I am having trouble even tho I have the exact code from the book.
This is what the plot looks like, but my question is how can I get it where there is no white space between the start and the end of the x-axis.
Here is the code:
import csv
from matplotlib import pyplot as plt
from datetime import datetime
# Get dates and high temperatures from file.
filename = 'sitka_weather_07-2014.csv'
with open(filename) as f:
reader = csv.reader(f)
header_row = next(reader)
#for index, column_header in enumerate(header_row):
#print(index, column_header)
dates, highs = [], []
for row in reader:
current_date = datetime.strptime(row[0], "%Y-%m-%d")
dates.append(current_date)
high = int(row[1])
highs.append(high)
# Plot data.
fig = plt.figure(dpi=128, figsize=(10,6))
plt.plot(dates, highs, c='red')
# Format plot.
plt.title("Daily high temperatures, July 2014", fontsize=24)
plt.xlabel('', fontsize=16)
fig.autofmt_xdate()
plt.ylabel("Temperature (F)", fontsize=16)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.show()
There is an automatic margin set at the edges, which ensures the data to be nicely fitting within the axis spines. In this case such a margin is probably desired on the y axis. By default it is set to 0.05 in units of axis span.
To set the margin to 0 on the x axis, use
plt.margins(x=0)
or
ax.margins(x=0)
depending on the context. Also see the documentation.
In case you want to get rid of the margin in the whole script, you can use
plt.rcParams['axes.xmargin'] = 0
at the beginning of your script (same for y of course). If you want to get rid of the margin entirely and forever, you might want to change the according line in the matplotlib rc file:
axes.xmargin : 0
axes.ymargin : 0
Example
import seaborn as sns
import matplotlib.pyplot as plt
tips = sns.load_dataset('tips')
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))
tips.plot(ax=ax1, title='Default Margin')
tips.plot(ax=ax2, title='Margins: x=0')
ax2.margins(x=0)
Alternatively, use plt.xlim(..) or ax.set_xlim(..) to manually set the limits of the axes such that there is no white space left.
If you only want to remove the margin on one side but not the other, e.g. remove the margin from the right but not from the left, you can use set_xlim() on a matplotlib axes object.
import seaborn as sns
import matplotlib.pyplot as plt
import math
max_x_value = 100
x_values = [i for i in range (1, max_x_value + 1)]
y_values = [math.log(i) for i in x_values]
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))
sn.lineplot(ax=ax1, x=x_values, y=y_values)
sn.lineplot(ax=ax2, x=x_values, y=y_values)
ax2.set_xlim(-5, max_x_value) # tune the -5 to your needs
The code below creates a bar plot with an inverted y-axis. What I don't manage yet is that the bars do not "hang from above" but start at the bottom. In other words, I like the bars to start at the maximum value of the y axis (i.e. at the x-axis) and ending at the value of df['y']. How can I do that?
import pandas as pd
import matplotlib.pyplot as plt
df = pd.DataFrame(data={'x_cat': ['aaaaa',
'bvvvvvv',
'deeeee',
'qqqqqqq',
'rr rrrrrrrr',
'rss sdasr',
'cccccccccccc',
'aarrrrrrrrrrra'
],
'y': [11.91,
35.19,
43.61,
46.12,
75.03,
81.39,
83.28,
89.20]
})
df['rank'] = df['y'].rank(method='dense') - 1
fig = plt.figure()
ax = fig.add_subplot(111)
# increase space below subplot
fig.subplots_adjust(bottom=0.3)
ax.bar(df['rank'],
df['y'],
width=0.8,
)
# invert y axis
ax.invert_yaxis()
# label x axis
ax.set_xticks(range(len(df)))
ax.set_xticklabels(df['x_cat'],
fontdict={'fontsize': 14})
for tick in ax.get_xticklabels():
tick.set_rotation(90)
You would need to calculate the new bottom. (Note that
because the axis is inverted, the "bottom" becomes the visual top of the bars.) The bottom is the value, the height is maximum minus the value itself.
I changed some other aspects of your plot, e.g. if your values are not sorted, calculating the rank and using it for plotting would result in wrong labelling. Hence better sort the dataframe beforehands (and forget about the rank).
Finally, we would need to adjust the "sticky edges" of the bars, because they should sit tight to the bottom of the figure (i.e. the top of the axis).
import pandas as pd
import matplotlib.pyplot as plt
df = pd.DataFrame({'x_cat': ['aaaaa', 'bvvvvvv', 'deeeee', 'qqqqqqq', 'rr rrrrrrrr',
'rss sdasr', 'cccccccccccc', 'aarrrrrrrrrrra'],
'y': [11.91, 35.19, 43.61, 46.12, 75.03, 81.39, 83.28, 89.20]})
df.sort_values("y", inplace=True)
fig = plt.figure()
ax = fig.add_subplot(111)
# increase space below subplot
fig.subplots_adjust(bottom=0.3)
bars = ax.bar(df['x_cat'], df['y'].max()-df['y'], bottom=df['y'], width=0.8, )
# invert y axis
ax.invert_yaxis()
ax.tick_params(axis="x", rotation=90, labelsize=14)
for bar in bars:
bar.sticky_edges.y[:] = [df['y'].values.max()]
ax.autoscale()
plt.show()
considering the following pandas DataFrame:
labels values_a values_b values_x values_y
0 date1 1 3 150 170
1 date2 2 6 200 180
It is easy to plot this with Seaborn (see example code below). However, due to the big difference between values_a/values_b and values_x/values_y, the bars for values_a and values_b are not easily visible (actually, the dataset given above is just a sample and in my real dataset the difference is even bigger). Therefore, I would like to use two y-axis, i.e., one y-axis for values_a/values_b and one for values_x/values_y. I tried to use plt.twinx() to get a second axis but unfortunately, the plot shows only two bars for values_x and values_y, even though there are at least two y-axis with the right scaling. :) Do you have an idea how to fix that and get four bars for each label whereas the values_a/values_b bars relate to the left y-axis and the values_x/values_y bars relate to the right y-axis?
Thanks in advance!
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
columns = ["labels", "values_a", "values_b", "values_x", "values_y"]
test_data = pd.DataFrame.from_records([("date1", 1, 3, 150, 170),\
("date2", 2, 6, 200, 180)],\
columns=columns)
# working example but with unreadable values_a and values_b
test_data_melted = pd.melt(test_data, id_vars=columns[0],\
var_name="source", value_name="value_numbers")
g = sns.barplot(x=columns[0], y="value_numbers", hue="source",\
data=test_data_melted)
plt.show()
# values_a and values_b are not displayed
values1_melted = pd.melt(test_data, id_vars=columns[0],\
value_vars=["values_a", "values_b"],\
var_name="source1", value_name="value_numbers1")
values2_melted = pd.melt(test_data, id_vars=columns[0],\
value_vars=["values_x", "values_y"],\
var_name="source2", value_name="value_numbers2")
g1 = sns.barplot(x=columns[0], y="value_numbers1", hue="source1",\
data=values1_melted)
ax2 = plt.twinx()
g2 = sns.barplot(x=columns[0], y="value_numbers2", hue="source2",\
data=values2_melted, ax=ax2)
plt.show()
This is probably best suited for multiple sub-plots, but if you are truly set on a single plot, you can scale the data before plotting, create another axis and then modify the tick values.
Sample Data
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
import numpy as np
columns = ["labels", "values_a", "values_b", "values_x", "values_y"]
test_data = pd.DataFrame.from_records([("date1", 1, 3, 150, 170),\
("date2", 2, 6, 200, 180)],\
columns=columns)
test_data_melted = pd.melt(test_data, id_vars=columns[0],\
var_name="source", value_name="value_numbers")
Code:
# Scale the data, just a simple example of how you might determine the scaling
mask = test_data_melted.source.isin(['values_a', 'values_b'])
scale = int(test_data_melted[~mask].value_numbers.mean()
/test_data_melted[mask].value_numbers.mean())
test_data_melted.loc[mask, 'value_numbers'] = test_data_melted.loc[mask, 'value_numbers']*scale
# Plot
fig, ax1 = plt.subplots()
g = sns.barplot(x=columns[0], y="value_numbers", hue="source",\
data=test_data_melted, ax=ax1)
# Create a second y-axis with the scaled ticks
ax1.set_ylabel('X and Y')
ax2 = ax1.twinx()
# Ensure ticks occur at the same positions, then modify labels
ax2.set_ylim(ax1.get_ylim())
ax2.set_yticklabels(np.round(ax1.get_yticks()/scale,1))
ax2.set_ylabel('A and B')
plt.show()
I am trying to find a way to move the little multiplier below the x-axis to the top. I have a plot with two x-axis and the multiplier of the top axis is placed below the bottom x-axis, which I find confusing.
Here is a small example:
import matplotlib as mpl
import matplotlib.pyplot as plt
import seaborn as sns
fig = plt.figure(num=None,figsize=(15, 2.5), dpi=300)
gs = mpl.gridspec.GridSpec(1,1)
ax2 = plt.subplot(gs[0,0])
ax1 = ax2.twiny()
ax1.grid(False)
ax1.set_xlim(0,10000000)
ax2.set_xlim(0,1000000)
ax1.set_ylim([0,100])
ax2.set_ylim([0,100])
plt.show()
Now, if you change ax2.set_xlim(0,1000000) to ax2.set_xlim(0,100000000), then both multipliers are placed below the bottom x-axis. Maybe it is also possible to prevent the multiplier from overlapping with the x-axis tick labels?
My problem with researching this is that I have no idea how this 'multiplier' is actually called.