I am drawing 4 sets of 12 plots (i.e. 48 plots in total). I want to combine the 12 plots within each of the 4 sets into one figure. However, I do not know how to combine the plots. At the moment, I am only drawing the 48 plots.
The dictionary I am referring to in the following contains 4 dictionaries, in turn containing 12 datasets each:
import matplotlib.pyplot as plt
import seaborn as sns
for j in dic:
for i in dic[j]:
df = pd.concat(dic[j][i].values(), ignore_index=True)
var = np.random.normal(loc=0, scale=1, size=10000)
fig, ax = plt.subplots()
sns.histplot(df['Z'], stat='density', ax=ax)
sns.kdeplot(var, color='r', ax=ax)
Thanks to #GenG, I found the solution:
temp_1 = [0,1,2,0,1,2,0,1,2,0,1,2]
temp_2 = [0,0,0,1,1,1,2,2,2,3,3,3]
for j in dic:
fig, ax = plt.subplots(3,4)
for i, t, k in zip(dic[j], temp_1, temp_2):
df = pd.concat(dic[j][i].values(), ignore_index=True)
var = np.random.normal(loc=0, scale=1, size=10000)
sns.histplot(df['Z'], stat='density', ax=ax[t][k])
sns.kdeplot(var, color='r', ax=ax[t][k])
Related
Here is the code for creating subplots and the graphs look like what's shown in the attached image. Is there a way to adjust the graphs so they look good and identical in size?
import matplotlib.pyplot as plt
df = pd.read_csv(path)
merged = df[df["test"].isin(['viral_load'])]
g = merged.groupby('id')
fig, axes = plt.subplots(g.ngroups, sharex=True, figsize=(8, 6))
for i, (id, d) in enumerate(g):
ax = d.plot.line(x='months', y='result', ax=axes[i], title=id)
ax.legend().remove()
fig.tight_layout()
Here you can see the result of the above code.
I'm trying to print a grid of seaborn plots by using a list of column names. It is plotting everything on the last plot.
fig, ax = plt.subplots(2,2, figsize=(10,10), sharex=True)
for aa in listA:
for j in range(2):
for i in range(2):
ax[i][j] = sns.lineplot(x='time', y=aa, data=new_df, color='r')
Output plot
One way is to flatten the ax and iterate through them. You can call plt from matplotlib directly:
import pandas as pd
import matplotlib. pyplot as plt
dates = pd.date_range(start='1/1/2018', periods=10, freq='1D')
listA = [np.random.normal(0,1,10) for i in range(4)]
new_df = pd.DataFrame({'time':dates })
fig, ax = plt.subplots(2,2, figsize=(10,10), sharex=True)
ax = ax.flatten()
for i,aa in enumerate(listA):
ax[i].plot(new_df['time'], aa, color='r')
ax[i].set_xticklabels(new_df['time'], rotation = 45, ha="right",fontsize=7)
If you really wanna use seaborn, it goes like:
fig, ax = plt.subplots(2,2, figsize=(10,10), sharex=True)
ax = ax.flatten()
for i,aa in enumerate(listA):
sns.lineplot(x=new_df['time'], y=aa, color='r',ax=ax[i])
ax[i].set_xticklabels(new_df['time'], rotation = 45, ha="right",fontsize=7)
Try replacing the following line of code:
ax[i][j] = sns.lineplot(x='time', y=aa, data=new_df, color='r')
with the following:
ax[i,j] = sns.lineplot(x='time', y=aa, data=new_df, color='r')
fig, ax = plt.subplots(2,2, figsize=(15,5))
a = 0
for j in range(2):
for i in range(2):
sns.lineplot(x='time', y=listA[a], data=new_df[new_df['transportation_type']=='walking'], ax=ax[i,j])
a+=1
This question is related to group multiple plot in one figure python, "individual 28 plots".
This is my code:
for column in df.columns[1:]:
sns.set()
fig, ax = plt.subplots(nrows=3, ncols=3) # tried 9 plots in one figure
sns.set(style="whitegrid")
sns.swarmplot(x='GF', y=column, data=df,order=["WT", 'Eulomin']) # Choose column
sns.despine(offset=10, trim=True) #?
plt.savefig('{}.png'.format(column), bbox_inches='tight') # filename
plt.show()
I have more than 100 columns and it saves every file individually and just prints empty plots beside the normal one . How do I save 9 plots in one figure, till it reachs the moment he'll have 5 left (which will have to be in one figure either)?
Instead of iterating through columns, iterate through multiples of 9 with range to index the data frame by column number while placing each swarmplot into the ax array you define:
from itertools import product
...
sns.set(style="whitegrid")
for i in range(1, 100, 9): # ITERATE WITH STEPS
col = i
fig, ax = plt.subplots(nrows=3, ncols=3, figsize = (12,6))
# TRAVERSE 3 X 3 MATRIX
for r, c in product(range(3), range(3)):
if col in range(len(df.columns)): # CHECK IF COLUMN EXISTS
# USE ax ARGUMENT WITH MATRIX INDEX
sns.swarmplot(x='GF', y=df[df.columns[col]], data=df, ax=ax[r,c],
order=["WT", 'Eulomin'])
sns.despine(offset=10, trim=True)
col += 1
plt.tight_layout()
plt.savefig('SwarmPlots_{0}-{1}.png'.format(i,i+8), bbox_inches='tight')
To demonstrate with random, seeded data of 100 columns by 500 rows for reproducibility:
Data
import numpy as np
import pandas as pd
np.random.seed(362020)
cols = ['Col'+str(i) for i in range(1,100)]
df = (pd.DataFrame([np.random.randn(99) for n in range(500)])
.assign(GF = np.random.choice(['r', 'python', 'julia'], 500))
.set_axis(cols + ['GF'], axis='columns', inplace = False)
.reindex(['GF'] + cols, axis='columns')
)
df.shape
# (500, 100)
Plot
import matplotlib.pyplot as plt
import seaborn as sns
from itertools import product
sns.set(style="whitegrid")
for i in range(1, 100, 9):
col = i
fig, ax = plt.subplots(nrows=3, ncols=3, figsize = (12,6))
for r, c in product(range(3), range(3)):
if col in range(len(df.columns)):
sns.swarmplot(x='GF', y=df[df.columns[col]], data=df, ax=ax[r,c])
col += 1
plt.tight_layout()
plt.savefig('SwarmPlots_{0}-{1}.png'.format(i,i+8), bbox_inches='tight')
plt.show()
plt.clf()
plt.close()
Output (first plot)
I'm plotting a CSV file from my simulation results. The plot has three graphs in the same figure fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(24, 6)).
However, for comparison purposes I want the y-axis in all graphs starting at zero and the ending at a specific value. I tried the solution mentioned here from the Seaborn author. I don't get any errors, but the solution also does not work for me.
Here's my script:
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
fname = 'results/filename.csv'
def plot_file():
fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(24, 6))
df = pd.read_csv(fname, sep='\t')
profits = \
df.groupby(['providerId', 'periods'], as_index=False)['profits'].sum()
# y-axis needs to start at zero and end at 10
g = sns.lineplot(x='periods',
y='profits',
data=profits,
hue='providerId',
legend='full',
ax=axes[0])
# y-axis need to start at zero and end at one
g = sns.scatterplot(x='periods',
y='price',
hue='providerId',
style='providerId',
data=df,
legend=False,
ax=axes[1])
# y-axis need to start at zero and end at one
g = sns.scatterplot(x='periods',
y='quality',
hue='providerId',
style='providerId',
data=df,
legend=False,
ax=axes[2])
g.set(ylim=(0, None))
plt.show()
print(g) # -> AxesSubplot(0.672059,0.11;0.227941x0.77)
The resulting figure is as follows:
How can I adjust each individual plot?
Based on the way you've written your code, you can refer to each subplot axis with g.axis and use g.axis.set_ylim(low,high). (A difference compared to the linked answer is that your graphs are not being plotted on a seaborn FacetGrid.)
An example using dummy data and different axis ranges to illustrate:
df = pd.DataFrame(np.random.uniform(0,10,(100,2)), columns=['a','b'])
fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(8,4))
g = sns.lineplot(x='a',
y='b',
data=df.sample(10),
ax=axes[0])
g.axes.set_ylim(0,25)
g = sns.scatterplot(x='a',
y='b',
data=df.sample(10),
ax=axes[1])
g.axes.set_ylim(0,3.5)
g = sns.scatterplot(x='a',
y='b',
data=df.sample(10),
ax=axes[2])
g.axes.set_ylim(0,0.3)
plt.tight_layout()
plt.show()
I want to plot a series of seaborn heatmaps in a grid. I know the number of subplots (which can be odd or even).
The heatmaps will show the mean "occupation ratio" by "day of week" (y axis) and "hour of day" (x axis), e.g. they all share the same x / y domains.
Here's my current code:
df2 = df[['name','openLots','occupationRatio','DoW','Hour']]
fig, axs = plt.subplots(figsize=(24,24), nrows=7, ncols=6)
axs = axs.flatten()
locations = df2['name'].sort_values().unique()
def occupation_heatmap (name, ax):
dfn = df2[df2['name'] == name]
dfn = dfn.groupby(['DoW', 'Hour']).mean()['occupationRatio'].unstack()
dfn = dfn.reindex(['Mon', 'Tue', 'Wed','Thu','Fri','Sat','Sun'])
sns.heatmap(data=dfn, cmap="coolwarm", vmin=0, vmax=1.0, ax= ax)
ax.set_title(name)
i = 0
for n in locations:
occupation_heatmap (n, axs[i])
i = i+1
plt.tight_layout()
It looks almost like what I want (last few rows):
However want I want:
Have the y axis labels (DoW) only once per row (leftmost plot)
Have the colormap legend only on the rightmost plot in each row (or leave it out completely, the colors are pretty self-explainatory)
remove the "empty plots" in the last row because of an odd total number
Many thanks for any hints
Have the y axis labels (DoW) only once per row (leftmost plot)
This can be done using sharey = True as argument to plt.subplots.
Have the colormap legend only on the rightmost plot in each row (or leave it out completely, the colors are pretty self-explainatory)
Use the cbar = False argument to seaborn.heatmap in order not to show a colorbar. This can be given as an input to the plotting function in dependence of the actual number of subplots.
remove the "empty plots" in the last row because of an odd total number
After the loop for creating the plots you may add another loop removing the unused axes.
for j in range(len(locations), ncols*nrows):
axs[j].axis("off")
Here is a complete example (where I borrowed the cod to generate a dataframe from #Robbie):
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import seaborn as sns
days = ['Mon','Tue','Wed','Thurs','Fri','Sat','Sun']
names = ["Parkhaus {:02}".format(i+1) for i in range(22)]
nItems = 1000
df = pd.DataFrame()
df['name'] = [names[i] for i in np.random.randint(0,len(names),nItems)]
df['openLots'] = np.random.randint(0,100,nItems)
df['occupationRatio'] = np.random.rand(nItems)
df['DoW'] = [days[i] for i in np.random.randint(0,7,nItems)]
df['Hour'] = np.random.randint(0,12,nItems)
df2 = df[['name','openLots','occupationRatio','DoW','Hour']]
nrows = 4; ncols=6
fig, axs = plt.subplots(nrows=nrows, ncols=ncols, figsize=(15,9), sharey=True)
axs = axs.flatten()
locations = df2['name'].sort_values().unique()
def occupation_heatmap (name, ax, cbar=False, ylabel=False):
dfn = df2[df2['name'] == name]
dfn = dfn.groupby(['DoW', 'Hour']).mean()['occupationRatio'].unstack()
dfn = dfn.reindex(['Mon', 'Tue', 'Wed','Thu','Fri','Sat','Sun'])
sns.heatmap(data=dfn, cmap="coolwarm", vmin=0, vmax=1.0, ax=ax, cbar=cbar)
ax.set_title(name)
plt.setp(ax.get_yticklabels(), rotation=0)
if not ylabel: ax.set_ylabel("")
for i, n in enumerate(locations):
occupation_heatmap (n, axs[i], cbar=i%ncols==ncols-1, ylabel=i%ncols==0)
for j in range(len(locations), ncols*nrows):
axs[j].axis("off")
plt.tight_layout()
plt.show()
You can be more flexible and just create an axis for each name present, something like this:
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import seaborn as sns
import string
days = ['Mon','Tue','Wed','Thurs','Fri','Sat','Sun']
names = [string.lowercase[i] for i in range(22)]
nItems = 1000
df = pd.DataFrame()
df['name'] = [names[i] for i in np.random.randint(0,len(names),nItems)]
df['openLots'] = np.random.randint(0,100,nItems)
df['occupationRatio'] = np.random.randint(0,100,nItems)
df['DoW'] = [days[i] for i in np.random.randint(0,7,nItems)]
df['Hour'] = np.random.randint(0,12,nItems)
fig = plt.figure(figsize=(12,12))
for index, name in enumerate(names):
ax = fig.add_subplot(4,6,index+1)
dfn = df.loc[df.name==name]
dfn = dfn.groupby(['DoW','Hour']).mean()['occupationRatio'].unstack()
dfn = dfn.reindex(days)
# Now we can operate on each plot axis individually
if index%6!=5: #i.e.
# Don't draw a colorbar
sns.heatmap(data = dfn, cmap='coolwarm', ax=ax, cbar=False)
else:
sns.heatmap(data = dfn, cmap='coolwarm', ax=ax)
if index%6!=0:
# Remove the y-axis label
ax.set_ylabel('')
ax.set_yticks(())
ax.set_title(name)
fig.tight_layout()
fig.show()
Results in:
You could also play around with the x-axes (for example remove labels and ticks except for the bottom row).