plotting whit subplots in a loop python [duplicate] - python

Case:
I receive a dataframe with (say 50) columns.
I extract the necessary columns from that dataframe using a condition.
So we have a list of selected columns of our dataframe now. (Say this variable is sel_cols)
I need a bar chart for each of these columns value_counts().
And I need to arrange all these bar charts in 3 columns, and varying number of rows based on number of columns selected in sel_cols.
So, if say 8 columns were selected, I want the figure to have 3 columns and 3 rows, with last subplot empty or just 8 subplots in 3x3 matrix if that is possible.
I could generate each chart separately using following code:
for col in sel_cols:
df[col].value_counts().plot(kind='bar)
plt.show()
plt.show() inside the loop so that each chart is shown and not just the last one.
I also tried appending these charts to a list this way:
charts = []
for col in sel_cols:
charts.append(df[col].value_counts().plot(kind='bar))
I could convert this list into an numpy array through reshape() but then it will have to be perfectly divisible into that shape. So 8 chart objects will not be reshaped into 3x3 array.
Then I tried creating the subplots first in this way:
row = len(sel_cols)//3
fig, axes = plt.subplots(nrows=row,ncols=3)
This way I would get the subplots, but I get two problems:
I end up with extra subplots in the 3 columns which will go unplotted (8 columns example).
I do not know how to plot under each subplots through a loop.
I tried this:
for row in axes:
for chart, col in zip(row,sel_cols):
chart = data[col].value_counts().plot(kind='bar')
But this only plots the last subplot with the last column. All other subplots stays blank.
How to do this with minimal lines of code, possibly without any need for human verification of the final subplots placements?
You may use this sample dataframe:
pd.DataFrame({'A':['Y','N','N','Y','Y','N','N','Y','N'],
'B':['E','E','E','E','F','F','F','F','E'],
'C':[1,1,0,0,1,1,0,0,1],
'D':['P','Q','R','S','P','Q','R','P','Q'],
'E':['E','E','E','E','F','F','G','G','G'],
'F':[1,1,0,0,1,1,0,0,1],
'G':['N','N','N','N','Y','N','N','Y','N'],
'H':['G','G','G','E','F','F','G','F','E'],
'I':[1,1,0,0,1,1,0,0,1],
'J':['Y','N','N','Y','Y','N','N','Y','N'],
'K':['E','E','E','E','F','F','F','F','E'],
'L':[1,1,0,0,1,1,0,0,1],
})
Selected columns are: sel_cols = ['A','B','D','E','G','H','J','K']
Total 8 columns.
Expected output is bar charts for value_counts() of each of these columns arranged in subplots in a figure with 3 columns. Rows to be decided based on number of columns selected, here 8 so 3 rows.

Given OP's sample data:
df = pd.DataFrame({'A':['Y','N','N','Y','Y','N','N','Y','N'],'B':['E','E','E','E','F','F','F','F','E'],'C':[1,1,0,0,1,1,0,0,1],'D':['P','Q','R','S','P','Q','R','P','Q'],'E':['E','E','E','E','F','F','G','G','G'],'F':[1,1,0,0,1,1,0,0,1],'G':['N','N','N','N','Y','N','N','Y','N'],'H':['G','G','G','E','F','F','G','F','E'],'I':[1,1,0,0,1,1,0,0,1],'J':['Y','N','N','Y','Y','N','N','Y','N'],'K':['E','E','E','E','F','F','F','F','E'],'L':[1,1,0,0,1,1,0,0,1]})
sel_cols = list('ABDEGHJK')
data = df[sel_cols].apply(pd.value_counts)
We can plot the columns of data in several ways (in order of simplicity):
DataFrame.plot with subplots param
seaborn.catplot
Loop through plt.subplots
1. DataFrame.plot with subplots param
Set subplots=True with the desired layout dimensions. Unused subplots will be auto-disabled:
data.plot.bar(subplots=True, layout=(3, 3), figsize=(8, 6),
sharex=False, sharey=True, legend=False)
plt.tight_layout()
2. seaborn.catplot
melt the data into long-form (i.e., 1 variable per column, 1 observation per row) and pass it to seaborn.catplot:
import seaborn as sns
melted = data.melt(var_name='var', value_name='count', ignore_index=False).reset_index()
sns.catplot(data=melted, kind='bar', x='index', y='count',
col='var', col_wrap=3, sharex=False)
3. Loop through plt.subplots
zip the columns and axes to iterate in pairs. Use the ax param to place each column onto its corresponding subplot.
If the grid size is larger than the number of columns (e.g., 3*3 > 8), disable the leftover axes with set_axis_off:
fig, axes = plt.subplots(3, 3, figsize=(8, 8), constrained_layout=True, sharey=True)
# plot each col onto one ax
for col, ax in zip(data.columns, axes.flat):
data[col].plot.bar(ax=ax, rot=0)
ax.set_title(col)
# disable leftover axes
for ax in axes.flat[data.columns.size:]:
ax.set_axis_off()

Alternative to the answer by tdy, I tried to do it without seaborn using Matplotlib and a for loop.
Figured it might be better for some who want specific control over subplots with formatting and other parameters, then this is another way:
fig = plt.figure(1,figsize=(16,12))
for i, col in enumerate(sel_cols,1):
fig.add_subplot(3,4,i,)
data[col].value_counts().plot(kind='bar',ax=plt.gca())
plt.title(col)
plt.tight_layout()
plt.show(1)
plt.subplot activates a subplot, while plt.gca() points to the active subplot.

Related

Creating a single tidy seaborn plot in a 'for' loop

I'm trying to generate a plot in seaborn using a for loop to plot the contents of each dataframe column on its own row.
The number of columns that need plotting can vary between 1 and 30. However, the loop creates multiple individual plots, each with their own x-axis, which are not aligned and with a lot of wasted space between the plots. I'd like to have all the plots together with a shared x-axis without any vertical spacing between each plot that I can then save as a single image.
The code I have been using so far is below.
comp_relflux = measurements.filter(like='rel_flux_C', axis=1) *# Extracts relevant columns from larger dataframe
comp_relflux=comp_relflux.reindex(comp_relflux.mean().sort_values().index, axis=1) # Sorts into order based on column mean.
plt.rcParams["figure.figsize"] = [12.00, 1.00]
for column in comp_relflux.columns:
plt.figure()
sns.scatterplot((bjd)%1, comp_relflux[column], color='b', marker='.')
This is a screenshot of the resultant plots.
I have also tried using FacetGrid, but this just seems to plot the last column's data.
p = sns.FacetGrid(comp_relflux, height=2, aspect=6, despine=False)
p.map(sns.scatterplot, x=(bjd)%1, y=comp_relflux[column])
To combine the x-axis labels and have just one instead of having it for each row, you can use sharex. Also, using plt.subplot() to the number of columns you have, you would also be able to have just one figure with all the subplots within it. As there is no data available, I used random numbers below to demonstrate the same. There are 4 columns of data in my df, but have kept as much of your code and naming convention as is. Hope this is what you are looking for...
comp_relflux = pd.DataFrame(np.random.rand(100, 4)) #Random data - 4 columns
bjd=np.linspace(0,1,100) # Series of 100 points - 0 to 1
rows=len(comp_relflux.columns) # Use this to get column length = subplot length
fig, ax = plt.subplots(rows, 1, sharex=True, figsize=(12,6)) # The subplots... sharex is assigned here and I move the size in here from your rcParam as well
for i, column in enumerate(comp_relflux.columns):
sns.scatterplot((bjd)%1, comp_relflux[column], color='b',marker='.', ax=ax[i])
1 output plot with 4 subplots

Plot bar chart in multiple subplot rows with Pandas

I have a simple long-form dataset I would like to generate bar charts from. The dataframe looks like this:
data = {'Year':[2019,2019,2019,2020,2020,2020,2021,2021,2021],
'Month_diff':[0,1,2,0,1,2,0,1,2],
'data': [12,10,13,16,12,18,19,45,34]}
df = pd.DataFrame(data)
I would like to plot a bar chart that has 3 rows, each for 2019, 2020 and 2021. X axis being month_diff and data goes on Y axis.
How do I do this?
If the data was in different columns, then I could have just used this code:
df.plot(x="X", y=["A", "B", "C"], kind="bar")
But my data is in a single column and ideally, I'd like to have different row for each year.
1. seaborn.catplot
The simplest option for a long-form dataframe is the seaborn.catplot wrapper, as Johan said:
import seaborn as sns
sns.catplot(data=df, x='Month_diff', y='data', row='Year',
kind='bar', height=2, aspect=4)
2. pivot + DataFrame.plot
Without seaborn:
pivot from long-form to wide-form (1 year per column)
use DataFrame.plot with subplots=True to put each year into its own subplot (and optionally sharey=True)
(df.pivot(index='Month_diff', columns='Year', values='data')
.plot.bar(subplots=True, sharey=True, legend=False))
plt.tight_layout()
Note that if you prefer a single grouped bar chart (which you alluded to at the end), you can just leave out the subplots param:
df.pivot(index='Month_diff', columns='Year', values='data').plot.bar()
3. DataFrame.groupby + subplots
You can also iterate the df.groupby('Year') object:
Create a subplots grid of axes based on the number of groups (years)
Plot each group (year) onto its own subplot row
groups = df.groupby('Year')
fig, axs = plt.subplots(nrows=len(groups), ncols=1, sharex=True, sharey=True)
for (name, group), ax in zip(groups, axs):
group.plot.bar(x='Month_diff', y='data', legend=False, ax=ax)
ax.set_title(name)
fig.supylabel('data')
plt.tight_layout()

Multiple boxplot with seaborn, but not within the same frame

In case of pandas boxplot, we could use:
for column in df:
plt.figure()
df.boxplot([column])
What could be done equivalently using seaborn, I want to plot multiple boxplots, but not in the same frame, rather for every column individually in loop
You can pass an Axes object to sns.boxplot:
for column in df:
fig, ax = plt.subplots()
sns.boxplot(df[column], ax=ax)

How to prepare 2 row, 5 column grid to plot 10 boxplot in one figure using seaborn or matplotlib library?

I am trying to plot a 10box plot in one image, in two rows, with the given code but no success, how can I implement this idea.
fig, axes =plt.subplots(2,5)
sns.set_style("darkgrid")
for i,t in enumerate(new_fs):
df = pd.read_csv(t,sep='\t')
sns.boxplot(data=df, orient='v',ax=axes[i % 2] )
Thank you.
As per the documentation for plt.subplots():
for NxM, subplots with N>1 and M>1 are returned as a 2D array.
Therefore, your variable axes is a 2D array, you need to access the individual axes using axes[i,j].
Alternatively, I would rewrite your for loop like so:
for t,ax in zip(new_fs, axes.flat):
df = pd.read_csv(t,sep='\t')
sns.boxplot(data=df, orient='v', ax=ax)

How to plot multiple dataframes to the same plot axes

I have two dataframes, with unique x and y coordinates, and I want to plot them in the same figure.
I am now plotting two dataframes in same figure as such:
plt.plot(df1['x'],df1['y'])
plt.plot(df2['x'],df2['y'])
plt.show
However, pandas also has plotting functionality.
df.plot()
How could I achieve the same as my first example but use the pandas functionality?
To plot all columns against the index as line plots.
ax = df1.plot()
df2.plot(ax=ax)
A single pandas.DataFrame.plot (not subplots=True) returns a matplotlib.axes.Axes, which you can then pass to the second dataframe.
To plot specific columns as x and y. Specifying x and y is required for scatter plots (kind='scatter').
ax = df1.plot(x='Lat', y='Lon', figsize=(8, 8))
df2.plot(ax=ax, x='Lat', y='Lon')

Categories

Resources