Modifying axis labels for pandas bar plot - python

I'm having difficulties adding percentages to my y-ticks. My total plot area includes 2 different plots, one is plotting percentages (df1), the other is not (df2). I've tried a variety of solutions I've found here on stackoverflow and other sites, but haven't had any luck.
import pandas as pd
df1 = pd.DataFrame([80.18, 50.0, 72.3, 90.1, 87.7])
df2 = pd.DataFrame([1000, 2000, 3000, 4000, 5000])
fig, axes = plt.subplots(nrows=1, ncols=2)
df1.plot.bar(ax=axes[0], title = "this", figsize=(8,8), legend=False)
df2.plot.bar(ax=axes[1], title="that", figsize=(8,8), legend=False)
Any help is appreciated

You can make use of a PercentFormatter [matplotlib-doc] for the yaxis:
from matplotlib.ticker import PercentFormatter
fig, axes = plt.subplots(nrows=1, ncols=2)
subplot = df1.plot.bar(ax=axes[0], title = "this", figsize=(8,8), legend=False)
subplot.yaxis.set_major_formatter(PercentFormatter())
df2.plot.bar(ax=axes[1], title="that", figsize=(8,8), legend=False)
For the given sample data, this produces:

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.ticker as mtick
df1 = pd.DataFrame([80.18, 50.0, 72.3, 90.1, 87.7])
df2 = pd.DataFrame([1000, 2000, 3000, 4000, 5000])
fig, axes = plt.subplots(nrows=1, ncols=2)
ax1 = df1.plot.bar(ax=axes[0], title = "this", figsize=(8,8), legend=False)
vals = ax1.get_yticks()
ax1.set_yticklabels(['{:.0%}'.format(x/100) for x in vals])
ax2 = df2.plot.bar(ax=axes[1], title="that", figsize=(8,8), legend=False)
vals2 = ax2.get_yticks()
ax2.set_yticklabels(['{:.0%}'.format(x/100) for x in vals2])
Picture

Related

matplotlib multiple Y-axis pandas plot

Could someone give me a tip on how to do multiple Y axis plots?
This is some made up data below, how could I put Temperature its own Y axis, Pressure on its own Y axis, and then have both Value1 and Value2 on the same Y axis. I am trying to go for the same look and feel of this SO post answer. Thanks for any tips, I don't understand ax3 = ax.twinx() process, like as far as do I need to define an ax.twinx() for each separate Y axis plot I need?
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
rows,cols = 8760,4
data = np.random.rand(rows,cols)
tidx = pd.date_range('2019-01-01', periods=rows, freq='H')
df = pd.DataFrame(data, columns=['Temperature','Value1','Pressure','Value2'], index=tidx)
# using subplots() function
fig, ax = plt.subplots(figsize=(25,8))
plt.title('Multy Y Plot')
ax2 = ax.twinx()
ax3 = ax.twinx()
ax4 = ax.twinx()
plot1, = ax.plot(df.index, df.Temperature)
plot2, = ax2.plot(df.index, df.Value1, color = 'r')
plot3, = ax3.plot(df.index, df.Pressure, color = 'g')
plot4, = ax4.plot(df.index, df.Value2, color = 'b')
ax.set_xlabel('Date')
ax.set_ylabel('Temperature')
ax2.set_ylabel('Value1')
ax3.set_ylabel('Pressure')
ax4.set_ylabel('Value2')
plt.legend([plot1,plot2,plot3,plot4],list(df.columns))
# defining display layout
plt.tight_layout()
# show plot
plt.show()
This will output everything jumbled up on the same side without separate Y axis for Pressure, Value1, and Value2.
You are adding 4 different plots in one, which is not helpful. I would recommend breaking it into 2 plots w/ shared x-axis "Date":
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
rows,cols = 8760,4
data = np.random.rand(rows,cols)
tidx = pd.date_range('2019-01-01', periods=rows, freq='H')
df = pd.DataFrame(data, columns=['Temperature','Value1','Pressure','Value2'], index=tidx)
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(25,8))
plt.title('Multy Y Plot')
ax1b = ax1.twinx()
plot1a, = ax1.plot(df.index, df.Temperature)
plot1b, = ax1b.plot(df.index, df.Pressure, color='r')
ax1.set_ylabel('Temperature')
ax1b.set_ylabel('Pressure')
ax2b = ax2.twinx()
plot2a, = ax2.plot(df.index, df.Value1, color='k')
plot2b, = ax2b.plot(df.index, df.Value2, color='g')
ax2.set_xlabel('Date')
ax2.set_ylabel('Value1')
ax2b.set_ylabel('Value2')
plt.legend([plot1a, plot1b, plot2a, plot2b], df.columns)
# defining display layout
plt.tight_layout()
# show plot
plt.show()
Here I have added in the first plot (on the top) Temperature and Pressure and on the second plot (on the bottom) Value 1 and Value 2. Normally, we add in the same plot things that make sense to compare on the same x-axis. Pressure and Temperature is a valid combination that is why I combined those two together. But you can do as you wish.
This answer below uses mpatches is how to make the subplot of Value1 and Value2 on the same axis. The solution for this post has subplot for Value1 and Value2 on different axis. Thanks for the help #tzinie!
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
rows,cols = 8760,4
data = np.random.rand(rows,cols)
tidx = pd.date_range('2019-01-01', periods=rows, freq='H')
df = pd.DataFrame(data, columns=['Temperature','Value1','Pressure','Value2'], index=tidx)
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(25,8))
plt.title('Multy Y Plot')
ax1b = ax1.twinx()
plot1a, = ax1.plot(df.index, df.Temperature, color='r') # red
plot1b, = ax1b.plot(df.index, df.Pressure, color='b') # blue
ax1.set_ylabel('Temperature')
ax1b.set_ylabel('Pressure')
ax2.plot(df.index, df.Value1, color='k') # black
ax2.plot(df.index, df.Value2, color='g') # green
ax2.set_xlabel('Date')
ax2.set_ylabel('Value1 & Value2')
red_patch = mpatches.Patch(color='red', label='Temperature')
blue_patch = mpatches.Patch(color='blue', label='Pressure')
green_patch = mpatches.Patch(color='green', label='Value2')
black_patch = mpatches.Patch(color='black', label='Value1')
plt.legend(handles=[red_patch,blue_patch,green_patch,black_patch])
# defining display layout
#plt.tight_layout()
# show plot
plt.show()

How to plot only max values using python

I want to make a graph about how the maximum value of a cluster of points at any given x coordinate changes over time.
What I have achieved so far:
What I want to achieve:
I was thinking that making a subset of the data with only the day and value, and then getting the maximum value of the array either by iterating trough it or using a function. But I don't know if it's possible like here:
Here's my code
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv('strong.csv', names=[
'time', 'exercise', 'set_number', 'mass', 'reps'],parse_dates=['time'])
df.time = pd.to_datetime(df.time,format='%Y-%m-%d')
df_exercise = df[(df.exercise == 'Bench Press (Barbell)')]
fig, ax = plt.subplots()
ax.scatter(
df_exercise.time,df_exercise.mass, c='Orange', s=30
)
ax.set(xlabel='Day', ylabel='Weight [ kg ]',
title='Time/Weight')
plt.xticks(fontsize=8,rotation=45)
plt.show()
plt.savefig('grafic.png')
You could group the dataframe by date and aggregate the maxima:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
df = pd.DataFrame({'time': np.repeat(pd.date_range('2021-03-01', periods=6), 2),
'mass': np.random.randint(20, 56, 12),
'excersie': 'Bench Press (Barbell)'})
df.time = pd.to_datetime(df.time, format='%Y-%m-%d')
df_exercise = df # just creating a dataframe similar to the question's
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(12, 5))
ax1.scatter(df_exercise.time, df_exercise.mass, c='limegreen', s=30)
df_plot = df_exercise.groupby('time')['mass'].agg('max')
ax2.scatter(df_exercise.time, df_exercise.mass, c='limegreen', s=30, alpha=0.3)
ax2.scatter(df_plot.index, df_plot.values, c='orange', s=30)
ax2.plot(df_plot.index, df_plot.values, c='black', lw=2, zorder=0)
for ax in (ax1, ax2):
ax.set(xlabel='Day', ylabel='Weight [ kg ]', title='Time/Weight')
ax.tick_params(axis='x', labelsize=8, labelrotation=45)
plt.tight_layout()
plt.show()

Share X axis between line and bar plot in Python's Matplotlib

I have the following script for generating a figure with two subplots: one line plot, and one bar plot.
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
plt.close('all')
np.random.seed(42)
n = 1000
idx = pd.date_range(end='2020-02-27', periods=n)
df = pd.Series(np.random.randint(-5, 5, n),
index=idx)
curve = df.cumsum()
bars = df.resample('M').sum()
fig = plt.figure()
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)
curve.plot(ax=ax1)
bars.plot(kind='bar', ax=ax2)
fig.set_tight_layout(True)
I would like to share the x axis between the two subplots, however the command ax2 = fig.add_subplot(212, sharex=ax1) will result in an empty graph for the line plot like the following figure.
Here is my version based on Matplotlib (without pandas api for plotting), may be it would be helpful.
I explicitly set the width of bars.
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
%matplotlib inline
plt.close('all')
np.random.seed(42)
n = 1000
idx = pd.date_range(end='2020-02-27', periods=n)
df = pd.Series(np.random.randint(-5, 5, n), index=idx)
curve = df.cumsum()
bars = df.resample('M').sum()
#fig = plt.figure()
#ax1 = fig.add_subplot(211)
#ax2 = fig.add_subplot(212)
#curve.plot(ax=ax1)
#bars.plot(kind='bar', ax=ax2)
fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True, gridspec_kw={'hspace': 0})
ax1.plot(curve.index, curve.values)
ax2.bar(bars.index, bars.values, width = (bars.index[0] - bars.index[1])/2)
fig.set_tight_layout(True)
_ = plt.xticks(bars.index, bars.index, rotation=90)

Matplotlib subplot title, figure title formatting

How would I go about formatting the below pie chart subplots so that there is more white-space between the fig title and subplot titles. Ideally the subplot titles would also be in closer vicinity to the actual pie chart itself.
I can't seem to find anything in the docs which might enable this, but I'm new to matplotlib.
import matplotlib.pyplot as plt
import pandas as pd
from pandas import DataFrame, Series
m = {"Men" : {"Yes": 2, "No": 8}}
w = {"Women": {"Yes": 3, "No": 7}}
data = {**m, **w}
df = DataFrame(data)
fig, axes = plt.subplots(1, len(df.columns))
fig.suptitle("Would you prefer to work from home?", fontsize=18)
logging.debug("fig.axes: {}".format(fig.axes))
for i, ax in enumerate(fig.axes):
col = df.ix[:, i]
ax = fig.axes[i]
pcnt_col = col / col.sum() * 100
ax.set_title("{} (n={})".format(pcnt_col.name, col.sum()))
ax.pie(pcnt_col.values, labels=pcnt_col.index,
autopct="%1.1f%%", startangle=90)
ax.axis("equal")
plt.legend(loc="lower right", title="Answer", fancybox=True,
ncol=1, shadow=True)
plt.show()
Use subplots_adjust to separate the two
plt.subplots_adjust(top=0.75)
import matplotlib.pyplot as plt
import pandas as pd
from pandas import DataFrame, Series
m = {"Men" : {"Yes": 2, "No": 8}}
w = {"Women": {"Yes": 3, "No": 7}}
data = {**m, **w}
df = DataFrame(data)
fig, axes = plt.subplots(1, len(df.columns))
fig.suptitle("Would you prefer to work from home?", fontsize=18)
logging.debug("fig.axes: {}".format(fig.axes))
for i, ax in enumerate(fig.axes):
col = df.ix[:, i]
ax = fig.axes[i]
pcnt_col = col / col.sum() * 100
ax.set_title("{} (n={})".format(pcnt_col.name, col.sum()))
ax.pie(pcnt_col.values, labels=pcnt_col.index,
autopct="%1.1f%%", startangle=90)
ax.axis("equal")
plt.legend(loc="lower right", title="Answer", fancybox=True,
ncol=1, shadow=True)
plt.subplots_adjust(top=0.55)
plt.show()

Creating legend in matplotlib after plotting two Pandas Series

I plotted two Pandas Series from the same DataFrame with the same x axis and everything worked out fine. However, when I tried to manually create a Legend, it appears but only with the title and not with the actually content. I've tried other solutions without any luck. Here's my code:
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()
width = .3
df.tally.plot(kind='bar', color='red', ax=ax1, width=width, position=1, grid=False)
df.costs.plot(kind='bar', color='blue', ax=ax2, width=width, position=0, grid=True)
ax1.set_ylabel('Tally')
ax2.set_ylabel('Total Cost')
handles1, labels1 = ax1.get_legend_handles_labels()
handles2, labels2 = ax2.get_legend_handles_labels()
plt.legend([handles1, handles2], [labels1, labels2], loc='upper left', title='Legend')
plt.show()
plt.clf()
Maybe you have a good reason to do it your way, but if not, this is much easier:
In [1]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# Optional, just better looking
import seaborn as sns
# Generate random data
df = pd.DataFrame(np.random.randn(10,3), columns=['tally', 'costs', 'other'])
df[['tally', 'costs']].plot(kind='bar', width=.3)
plt.show();
Out[1]:
Edit
After learning that this is because you have a much different scale for the other one, here's the pandas approach:
# Generate same data as Jianxun Li
np.random.seed(0)
df = pd.DataFrame(np.random.randint(50,100,(20,3)), columns=['tally', 'costs', 'other'])
df.costs = df.costs * 5
width = .3
df.tally.plot(kind='bar', color='#55A868', position=1, width=width, legend=True, figsize=(12,6))
df.costs.plot(kind='bar', color='#4C72B0', position=0, width=width, legend=True, secondary_y=True)
plt.show();
Something like this?
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# your data
# ===============================
np.random.seed(0)
df = pd.DataFrame(np.random.randint(50,100,(20,3)), columns=['col1', 'col2', 'col3'])
df.col2 = df.col2 * 5
# bar plot with twinx
# ===============================
fig, ax = plt.subplots()
width=0.3
ax.bar(df.index, df.col1, width=width, color='red', label='col1_data')
ax.legend(loc='best')
ax2 = ax.twinx()
ax2.bar(df.index+width, df.col2, width=width, color='blue', label='col2_data')
ax2.legend(loc='best')

Categories