Is there a way to merge two MatPlotLib plots together? - python

I have been trying to merge these two plots together but have not found a built-in in the documentation for MatPlotLib on how to do so. I want to show the two bar values next to each and for every new entry, add the new entry to the graph while shifting the other entries over to make space. The plots are below.
As stated prior, when I say merge, I do not simply mean just plop Plot A onto Plot B, but rather join the plots together so both bar values are shown in the same graph, like this:
The reasoning for this is that I will be able to log all the entries in a single plot without having to manually do so. By implementing something like this in my code, it would make entries go a lot quicker.
EDIT: I understand that I can graph these two together, but that is not what I am looking for. Once I get the necessary input, my program creates a graph of that data and saves it as a file. I am looking to append any new data to that original file by just shifting the original value over to the left in order to make space.
EDIT 2: How could I extract the data from each plot and after doing so, create a new graph? This would seem to be another acceptable workaround.

Is there anything preventing you from plotting each of them side by side but changing the index?
a, b, c = 2, 5, 3
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 1)
count = 0
ax.bar(count, a)
# if prgoram produces a new output then...
count += 1
ax.bar(count, b) # index means new bar plot has shifted
# again
count += 1
ax.bar(count, c) # shifted again
This should automatically expand the x-axis anyway. You may have to alter this slightly if you've particularly concenred about the width of these bars.
If this isn't what you wanted you could consider replotting with the bar container or even just stripping the height to reuse.
fig, ax = plt.subplots(1, 1)
count = 0
bar_cont = ax.bar(count, a) # reference to the bar container of interest
print(bar_cont.get_height())

import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_axes([0,0,1,1])
people = ['JOHN DOE', 'BOB SMITH']
values = [14,14]
ax.bar(people,values)
plt.show()
Should be the solution. You just have to pass a list instead of a single value to the plt.bar() function. More detailed explaination here.

Related

How to plot segments of a time series in a loop and only plot next iteration after user input?

I'm trying to plot a number of segments of a timeseries, let's say 5 segments.
I want each segment to be plotted individually and one after another after a given input (key press)
For example, 1) plot first segment, 2) wait for input and only after my input 3) plot next segment. I need python to wait for an input (key press) before plotting the next segment.
I've manged to almost make it work, but on jupyter notebook all figures are displayed at once only after I input something for all the plots (i.e. 5 inputs)
segments = segments.iloc[0:5] # reduced number for testing
list = []
for i in segments.itertuples(): # loop over df
f, ax = plt.subplots()
ax.plot(time, yy) # plot timeseries
plt.xlim([segments.start_time, segments.end_time]) # only show between limits
plt.show()
# get user input
a = input()
list.append(a) # add input to the list
I've been banging my head but haven't managed to solve this. Any suggestion on how to solve this issue?
I have one that works from adapting an example I had used before, but note that I don't use subplot here!:
import matplotlib.pyplot as plt
inp_ = []
for i in range(3):
labels = ['part_1','part_2','part_3']
pie_portions = [5,6,7]
plt.pie(pie_portions,labels=labels,autopct = '%1.1f%%')
plt.title(f'figure_no : {i+1}')
plt.show()
# get user input
a = input()
inp_.append(a) # add input to the list
If you use subplot, then you get what you are seeing where it waits to show them all at the end because the figure is only complete and available to display after the last subplot is specified. Otherwise it is blocked. The easiest solution is to switch away from using subplots, like in my block of code posted above.
If you needed it to absolutely work with subplot, you can in fact update the figure after, like so;
#Using subplots based on https://matplotlib.org/stable/gallery/pie_and_polar_charts/pie_demo2.html
import matplotlib.pyplot as plt
import numpy as np
def update_subplot():
'''
based on https://stackoverflow.com/a/36279629/8508004
'''
global fig, axs
ax_list = axs.ravel()
# ax_list[0] refers to the first subplot
ax_list[1].imshow(np.random.randn(100, 100))
#plt.draw()
# Some data
labels = 'Frogs', 'Hogs', 'Dogs', 'Logs'
fracs = [15, 30, 45, 10]
# Make figure and axes
fig, axs = plt.subplots(1, 3)
# A standard pie plot
axs[0].pie(fracs, labels=labels, autopct='%1.1f%%', shadow=True)
axs[1].axis('off') # based on https://stackoverflow.com/a/10035974/8508004
axs[2].axis('off')
plt.show()
import time
time.sleep(2)
update_subplot()
fig
However, if you run that, you'll see you get successive views with one plot and then two and the first (with just one of two subplots) stays around in the notebook output and so it is less than desirable.
Always best to provide a minimal reproducible example when posting your question. That way you get something close to what works for your case.
Also, it is a bad idea to use a built-in type as a name of variable. (list = []) It can lead to errors you aren't expecting later. Imagine you wanted to typecast a set back to a list later in your code example.
Compare:
list = []
my_set= {1,2,3}
a = list(my_set)
to
my_list = []
my_set= {1,2,3}
a = list(my_set)
The first will give TypeError: 'list' object is not callable.

How do I create a count plot with multiple columns without the axes being stored in a numpy.ndarray?

I'm new to coding and this is my first post. Sorry if it could be worded better!
I'm taking a free online course, and for one of the projects I have to make a count plot with 2 subplot columns.
I've managed to make a count plot with multiple subplots using the code below, and all of the values are correct.
fig = sns.catplot(x = 'variable', hue = 'value', order = ['active', 'alco', 'cholesterol', 'gluc', 'overweight', 'smoke'], col='cardio', data = df_cat, kind = 'count')
But because of the way I've done it, the fig.axes is stored in a 2 dimensional array. The only difference between both rows of the array is the title (cardio = 0 or cardio = 1). I'm assuming this is because of the col='cardio'. Does the col argument always cause the fig.axes to be stored in a 2D array? Is there a way around this or do I have to completely change how I'm making my graph?
I'm sure it's not usually a problem, but because of this, when I run my program through the test module, it fails since some of the functions in the test module don't work on numpy.ndarrays.
I pass the test if I change the reference from fig.axes[0] to fig.axes[0,0], but obviously I cant just change the test module to pass.
I found something. This is just an implementation detail, so it would be nuts to rely on it. If you set col_wrap, then you get an axes ndarray of a different shape.
Reproduced like this:
import seaborn as sns
# I don't have your data but I have this example
tips = sns.load_dataset("tips")
fig = sns.catplot(x='day', hue='sex', col='time', data=tips, kind='count', col_wrap=2)
fig.axes.shape
And it has shape (2,) i.e it's 1D. seaborn==0.11.2.

Populating Seaborn subplots using an array

The att_sales table has 3 fields item,qty and yr_mon. The buckets(i) function returns a list of 20 SKUs(list of item values). A group by function is used to find the monthly sales for each of these SKUs
and then churns out a violinplot. The exercise wors fine till this point.
I am trying to visualize the monthly sales for about 200 SKUs along 10 subplots.To do this I intended an iterator to run from 1 through 10 and populate each of the subplots.The code below populates the last subplot out of 10 empty ones. How do I go about achieving this?
fig, (axis1) = plt.subplots(5,2,figsize=(15,30))
plt.xticks(rotation=45)
s=att_sales[['item','qty','yr_mon']]
s=s[s.item.isin(buckets(i))]
s=s.groupby(['item','yr_mon'], as_index=False).qty.sum()
sns.violinplot(x="item", y="qty", data=s)
Edit1: On implmenting #Ted's solution I got an error min() arg is an empty sequence when the for loop ran from 0 to n. Changing the for loop to run between 1 and n, provides most the solution but not quite.
I need to know how to increase the size of the overall plot and of the individual subplots, and also change the orientation of the xticks to 45 degrees.
Here is a simplified example that I think you can tweak to make it work for you. I am using the tips dataset in seaborn and plotting 4 different violin plots based on what day it is. I have also created a buckets function that returns a single element list of one day.
When the figure is created with fig, axes = plt.subplots(2,2,figsize=(10,10)), it returns both a matplotlib figure object which is stored into fig and a 2 dimensional numpy array of matplotlib axes objects which is stored in axes. To get the top left plot you would do axes[0, 0]. If you wanted the bottom right hand plot you would do axes[1, 1]. If you created a 5 row by 2 column figure axes[3,0] would be the plot on the 4th row and first column.
# create function that will return a list of items
# this particular example returns just a list of one element
def buckets(i):
return [tips.day.unique()[i]]
# load dataset and create figure
tips = sns.load_dataset("tips")
num_plots = 4
fig, axes = plt.subplots(2,2,figsize=(10,10))
# iterate through all axes and create a violin plot
for i in range(num_plots):
df = tips[tips.day.isin(buckets(i))]
row = i // 2
col = i % 2
ax_curr = axes[row, col]
sns.violinplot(x="sex", y="tip", data=df, ax=ax_curr)
ax_curr.set_title(buckets(i))
Note that in this particular example you can use a facet grid which will do the same exact thing as what I did by plotting each day in a separate plot. You can take advantage of the facet grid if you label each bucket of SKUs a unique id. See the very last example on this page

how to change the colors of multiple subplots at once?

I am looping through a bunch of CSV files containing various measurements.
Each file might be from one of 4 different data sources.
In each file, I merge the data into monthly datasets, that I then plot in a 3x4 grid. After this plot has been saved, the loop moves on and does the same to the next file.
This part I got figured out, however I would like to add a visual clue to the plots, as to what data it is. As far as I understand it (and tried it)
plt.subplot(4,3,1)
plt.hist(Jan_Data,facecolor='Red')
plt.ylabel('value count')
plt.title('January')
does work, however this way, I would have to add the facecolor='Red' by hand to every 12 subplots. Looping through the plots wont work for this situation, since I want the ylabel only for the leftmost plots, and xlabels for the bottom row.
Setting facecolor at the beginning in
fig = plt.figure(figsize=(20,15),facecolor='Red')
does not work, since it only changes the background color of the 20 by 15 figure now, which subsequently gets ignored when I save it to a PNG, since it only gets set for screen output.
So is there just a simple setthecolorofallbars='Red' command for plt.hist(… or plt.savefig(… I am missing, or should I just copy n' paste it to all twelve months?
You can use mpl.rc("axes", color_cycle="red") to set the default color cycle for all your axes.
In this little toy example, I use the with mpl.rc_context block to limit the effects of mpl.rc to just the block. This way you don't spoil the default parameters for your whole session.
import matplotlib as mpl
import matplotlib.pylab as plt
import numpy as np
np.random.seed(42)
# create some toy data
n, m = 2, 2
data = []
for i in range(n*m):
data.append(np.random.rand(30))
# and do the plotting
with mpl.rc_context():
mpl.rc("axes", color_cycle="red")
fig, axes = plt.subplots(n, m, figsize=(8,8))
for ax, d in zip(axes.flat, data):
ax.hist(d)
The problem with the x- and y-labels (when you use loops) can be solved by using plt.subplots as you can access every axis seperately.
import matplotlib.pyplot as plt
import numpy.random
# creating figure with 4 plots
fig,ax = plt.subplots(2,2)
# some data
data = numpy.random.randn(4,1000)
# some titles
title = ['Jan','Feb','Mar','April']
xlabel = ['xlabel1','xlabel2']
ylabel = ['ylabel1','ylabel2']
for i in range(ax.size):
a = ax[i/2,i%2]
a.hist(data[i],facecolor='r',bins=50)
a.set_title(title[i])
# write the ylabels on all axis on the left hand side
for j in range(ax.shape[0]):
ax[j,0].set_ylabel(ylabel[j])
# write the xlabels an all axis on the bottom
for j in range(ax.shape[1]):
ax[-1,j].set_xlabel(xlabels[j])
fig.tight_layout()
All features (like titles) which are not constant can be put into arrays and placed at the appropriate axis.

Subplots in two separate figure windows inside one loop using matplotlib

I want to plot two separate quantities while running through a loop. I want to create a separate figure window for each quantity, such that each iteration of the loop creates one subplot for each quantity.
Basically, I want my code to do something like this:
import numpy as np
import matplotlib.pyplot as plt
nr = [10, 15, 20, 25, 35, 50]
fig1 = plt.figure(1)
fig2 = plt.figure(2)
for y in range(len(nr)):
m = np.arange(y+1)
n = (y+1)*np.arange(y+1)
fig1.subplot(3,2,y+1)
fig1.plot(m,n, 'b')
fig1.title('y=%s'%y)
m1 = np.square(np.arange(y+1))
n1 = (y+1)*np.arange(y+1)
fig2.subplot(3,2,y+1)
fig2.plot(m1,n1, 'r')
fig2.title('y=%s'%y)
fig1.show()
fig2.show()
This code doesn't work; gives me the error message that 'Figure' object has no attribute 'subplot'. I've tried many variations on this link - http://matplotlib.org/api/pyplot_api.html, but I am unable to understand how to do it the right way.
In the output, I want two figure windows, each with 6 subplots, such that the first one contains plots of m vs n, and the second one contains plots of m1 vs n1.
Okay, long explanation because there are multiple issues here.
The biggest problem you are running into is that there are multiple ways to handle things in matplotlib. In fact, there are effectively multiple interfaces. The easiest and most commonly used method is to just create your plot using pyplot and its methods like pyplot.subplot and pyplot.plot. This can work well for quick plots, but will not work well for your situation.
Since you want to create two figures and alternate plotting to those figures, you are going to want to use the more powerful objects in pyplot. You have gotten part way there yourself, but I'll try to help you with the last part.
You are good up until here:
import numpy as np
import matplotlib.pyplot as plt
nr = [10, 15, 20, 25, 35, 50]
fig1 = plt.figure(1)
fig2 = plt.figure(2)
for y in range(len(nr)):
m = np.arange(y+1)
n = (y+1)*np.arange(y+1)
but when you try to use the methods of Figure, you are getting confused and trying to use similar methods that belong to pyplot. The next portion should be rewritten as:
ax1 = fig1.add_subplot(3,2,y)
ax1.plot(m,n, 'b')
ax1.set_title('y=%s'%y)
m1 = np.square(np.arange(y+1))
n1 = (y+1)*np.arange(y+1)
ax2 = fig2.add_subplot(3,2,y)
ax2.plot(m1,n1, 'r')
ax2.set_title('y=%s'%y)
Here, what you have done is capture the Axes instance that is returned from add_subplot(). Then you plot onto the Axes instance. Also, when specifying where you want the subplot to be placed (the third input to Figure.add_subplot()), you do not want to use y+1 because that would start at 1 and end at 6 which would go out of the available range of 0-5. Remember that Python indices start with zero.
Finally, to show the figures you just created, you can either call pyplot.show() like this:
plt.show()
or you can save the figures to files like this:
fig1.savefig('fig1.png')
fig2.savefig('fig2.png')
The resulting figures look like this:

Categories

Resources