Double the amount of subplots when using twinx() in matplotlib - python

Here's my chart:
Unfortunately, this is there too, right below:
This is the code:
fig,ax1 = plt.subplots(6,1, figsize=(20,10),dpi=300)
fig2,ax2 = plt.subplots(6,1, figsize=(20,10),dpi=300)
for index, val in enumerate(datedf.columns):
g = ax1[index].plot(datedf.index, datedf[val], color=colors[index])
ax1[index].set(ylim=[-100,6500])
ax2[index] = ax1[index].twinx()
a = ax2[index].plot(qtydf.index, qtydf[val], color=colors[index], alpha=0.5)
ax2[index].set(ylim=[200,257000])
I tried this answer but I got an error on the first line (too many values to unpack)
Can anyone explain why?

You generate 2 figures, so you end up with 2 figures.
Instead you should do something like:
fig, axes = plt.subplots(6,1, figsize=(20,10),dpi=300)
for index, val in enumerate(datedf.columns):
ax1 = axes[index]
g = ax1.plot(datedf.index, datedf[val], color=colors[index])
ax1.set(ylim=[-100,6500])
ax2 = ax1.twinx()
ax2.plot(qtydf.index, qtydf[val], color=colors[index], alpha=0.5)
ax2.set(ylim=[200,257000])
NB. The code is untested as I don't have the original dataset.

Related

How to plot value counts for each subset in matplotlib/seaborn?

I am relatively new to matplotlib and there is probably a better way to deal with the problem. I have tried sns.countplot(), which does not have sorting option. So I tried to do it with a bar plot and pandas for counting:
my_data = pd.DataFrame({'actions': ['buy','buy','buy','observe','consult'] , 'places':['NY','AR','AR','NY','AR']})
fig, axs = plt.subplots(1, 2, figsize = (5,7))
axs = axs.ravel()
for place in my_data['places']:
x = 0
temp_df = my_data[my_data['places'] == place]
axs[x] = sns.barplot(y=temp_df.actions.value_counts().index, x=temp_df.actions.value_counts().values, color="#43B8E7",orient = 'h')
axs[x].set_title(place)
x=+1
where data look like
actions places
0 buy NY
1 buy AR
2 buy AR
3 observe NY
4 consult AR
and the code produces what's below. As you may have assumed, I need to plot NY as well, however, because of subsetting or something missed in the loop it does not work well. How to fix that? I feel that this is the easy one, however, cannot find it.
Are you looking for:
(my_data.groupby('places')['actions']
.value_counts().unstack('places')
.plot.bar(subplots=True)
)
Or similarly:
(pd.crosstab(my_data['actions'], my_data['places'])
.plot.bar(subplots=True)
)
Output:
If you want horizontal bars:
(pd.crosstab(my_data['actions'], my_data['places'])
.plot.barh(subplots=True, layout=[1,2])
)
Output:
Or we can fix your code:
fig, axs = plt.subplots(1, 2, figsize = (5,7))
axs = axs.ravel()
for ax,place in zip(axs,my_data['places'].unique()):
temp_df = my_data[my_data['places'] == place].actions.value_counts()
sns.barplot(y=temp_df.index, x=temp_df,
color="#43B8E7", ax=ax, orient = 'h')
ax.set_title(place)
Output (which isn't very well-aligned IMHO):
I would use a facetgrid since you're already using seaborn:
import pandas
import seaborn
axgrid = pandas.DataFrame({
'actions': ['buy','buy','buy','observe','consult'] ,
'places':['NY','AR','AR','NY','AR']
}).pipe((seaborn.catplot, 'data'),
y="actions", col="places",
order=['buy', 'consult', 'observe'],
kind="count"
)
And you get:

How to display all legends when plotting using *args & seaborn

My data & code are as below
w = [1,2,3,4,5,6,7,8,9,10]
vals = [[1,2,3,4,5,6,7,8,9,10],[2,4,6,8,8,8,8,8,7,1],[1,4,2,4,8,9,8,8,7,2]]
def plot_compare(*id_nums):
fig = plt.figure(figsize=(10, 5))
leg=[]
for id_num in id_nums:
rel = vals[id_num]
sns.lineplot(x=w, y=rel)
leg.append(id_num)
fig.legend(labels=[leg],loc=5,);
plot_compare(0,2)
The idea was to get multiple line plots with just one function (I my actual data I have a lot of values that need to be plotted)
When I run the code as above, I get the plot as below.
Line plots are exactly as I want, but the legend is just one item instead of 2 items (since I have plotted 2 line graphs).
I have tried moving the legend line inside of the for loop but no use. I want a may legends as the line plots.
Can anyone help?
You are having legend as list of list. Instead use fig.legend(labels=leg,loc=5)
Use:
w = [1,2,3,4,5,6,7,8,9,10]
vals = [[1,2,3,4,5,6,7,8,9,10],[2,4,6,8,8,8,8,8,7,1],[1,4,2,4,8,9,8,8,7,2]]
def plot_compare(*id_nums):
fig = plt.figure(figsize=(10, 5))
leg=[]
for id_num in id_nums:
rel = vals[id_num]
sns.lineplot(x=w, y=rel)
leg.append(id_num)
fig.legend(labels=leg,loc=5)
plt.show()
plot_compare(0,2)

Change titles in a for loop for plt.plot and create 6x16 subplots

secondHold = np.zeros((96,30))
channel = ['channel' for x in range(96)]
for i in range (96):
BlankBinsx = bins[blankposition,0:30,i]
StimBinsx = bins[NonBlankPositions,0:30,i]
meanx = BlankBinsx.mean(axis=0);
stimmeanx = StimBinsx.mean(axis=0);
for j in range(30):
hold[i][j] = meanx[j];
secondHold[i][j] = stimmeanx[j];
plt.subplots(1, 1, sharex='all', sharey='all')
plt.plot(hold[i], label='stimulus')
plt.plot(secondHold[i], label='Blank Stimulus')
plt.title('Channel x')
plt.xlabel('time (ms)')
plt.ylabel('Avg Spike Rate')
plt.legend()
plt.show()
I am creating 96 different graphs through a for-loop and I want it to also label the graphs (i.e., the first graph would be 'Channel 1', graph two 'Channel 2' and so on. I tried ax.set_title but couldn't figure it out how to make it work with the string and numbers.
Also I'd like the graphs to print as a 6x16 subplots instead of 96 graphs in a column.
You are creating a new figure each time in your for loop that's why you get 96 figures. I don't have your data so I can't provide a final figure but the following should work for you. The idea here is:
Define a figure and an array of axes containing 6x16 subplots.
Use enumerate on axes.flatten to iterate through the subfigures ax row wise and use i as the index to access the data.
Use the field specifier %d to label the subplots iteratively.
Put plt.show() outside the for loop
secondHold = np.zeros((96,30))
channel = ['channel' for x in range(96)]
fig, axes = plt.subplots(nrows=6, ncols=16, sharex='all', sharey='all')
for i, ax in enumerate(axes.flatten()):
BlankBinsx = bins[blankposition,0:30,i]
StimBinsx = bins[NonBlankPositions,0:30,i]
meanx = BlankBinsx.mean(axis=0);
stimmeanx = StimBinsx.mean(axis=0);
for j in range(30):
hold[i][j] = meanx[j];
secondHold[i][j] = stimmeanx[j];
ax.plot(hold[i], label='stimulus')
ax.plot(secondHold[i], label='Blank Stimulus')
ax.set_title('Channel %d' %i)
ax.set_xlabel('time (ms)')
ax.set_ylabel('Avg Spike Rate')
ax.legend()
plt.show()

How to make a stacked graph directly from a python groupby code?

I want to make a chart after applying groupby
So I have applied
Sales_comparison = SalesData[['Region', 'Sales2015',
'Sales2016']].groupby(['Region']).agg(['sum'])
I have tried for the graph
ax = Sales_comparison[['Sales2015','Sales2016']].plot(kind='bar', title
="Sales by region comparison", figsize=(7.5, 5), legend=True, fontsize=12)
ax.set_xlabel("Region", fontsize=12)
ax.set_ylabel("Sales", fontsize=12)
x = Sales_comparison.Region.index.tolist()
x_pos = [i for i, _ in enumerate(x)]
plt.xticks(x_pos, x)
plt.show()
But it is of no use
Is there any easier and shorter way to do what I want to achieve?
The data can be found in
Link for the data
Could you elaborate on what you mean by indexation?
I assume you mean labeling in the graph, which would work in a similar way that you have already done.
Sales_comparison = df[['Region', 'Sales2015', 'Sales2016']].groupby(['Region']).agg(['sum'])
ax = Sales_comparison.plot(kind='bar', stacked=True, legend=False,color=['navy','darkred'])
for i, label in enumerate(list(Sales_comparison.index)):
S16 = int(Sales_comparison.loc[label]['Sales2016'][0])
ax.annotate(str(S16),(i-0.2,S16+0.2*S16),color='white')
S15 = int(Sales_comparison.loc[label]['Sales2015'][0])
ax.annotate(str(S15),(i-0.2,S15-0.5*S15),color='white')
and result in the following image:
End Result with Labels

Combining FacetGrid and dual Y-axis in Pandas

I am trying to plot two different variables (linked by a relation of causality), delai_jour and date_sondage on a single FacetGrid. I can do it with this code:
g = sns.FacetGrid(df_verif_sum, col="prefecture", col_wrap=2, aspect=2, sharex=True,)
g = g.map(plt.plot, "date_sondage", "delai_jour", color="m", linewidth=2)
g = g.map(plt.bar, "date_sondage", "impossible")
which gives me this:
FacetGrid
(There are 33 of them in total).
I'm interested in comparing the patterns across the various prefecture, but due to the difference in magnitude I cannot see the changes in the line chart.
For this specific work, the best way to do it is to create a secondary y axis, but I can't seem to make anything work: it doesn't look like it's possible with FacetGrid, and I didn't understand the code not was able to replicate the examples i've seen with pure matplotlib.
How should I go about it?
I got this to work by iterating through the axes and plotting a secondary axis as in a typical Seaborn graph.
Using the OP example:
g = sns.FacetGrid(df_verif_sum, col="prefecture", col_wrap=2, aspect=2, sharex=True)
g = g.map(plt.plot, "date_sondage", "delai_jour", color="m", linewidth=2)
for ax, (_, subdata) in zip(g.axes, df_verif_sum.groupby('prefecture')):
ax2=ax.twinx()
subdata.plot(x='data_sondage',y='impossible', ax=ax2,legend=False,color='r')
If you do any formatting to the x-axis, you may have to do it to both ax and ax2.
Here's an example where you apply a custom mapping function to the dataframe of interest. Within the function, you can call plt.gca() to get the current axis at the facet being currently plotted in FacetGrid. Once you have the axis, twinx() can be called just like you would in plain old matplotlib plotting.
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import seaborn as sns
def facetgrid_two_axes(*args, **kwargs):
data = kwargs.pop('data')
dual_axis = kwargs.pop('dual_axis')
alpha = kwargs.pop('alpha', 0.2)
kwargs.pop('color')
ax = plt.gca()
if dual_axis:
ax2 = ax.twinx()
ax2.set_ylabel('Second Axis!')
ax.plot(data['x'],data['y1'], **kwargs, color='red',alpha=alpha)
if dual_axis:
ax2.plot(df['x'],df['y2'], **kwargs, color='blue',alpha=alpha)
df = pd.DataFrame()
df['x'] = np.arange(1,5,1)
df['y1'] = 1 / df['x']
df['y2'] = df['x'] * 100
df['facet'] = 'foo'
df2 = df.copy()
df2['facet'] = 'bar'
df3 = pd.concat([df,df2])
win_plot = sns.FacetGrid(df3, col='facet', size=6)
(win_plot.map_dataframe(facetgrid_two_axes, dual_axis=True)
.set_axis_labels("X", "First Y-axis"))
plt.show()
This isn't the prettiest plot as you might want to adjust the presence of the second y-axis' label, the spacing between plots, etc. but the code suffices to show how to plot two series of differing magnitudes within FacetGrids.

Categories

Resources