Need help in creating a function to plot a Matplotlib GridSpec - python-3.x

I have a dataset with 80 variables. I am interested in creating a function that will automate the creation of a 20 X 4 GridSpec in Matplotlib. Each subplot would either contain a histogram or a barplot for each of the 80 variables in the data. As a first step, I successfully created two functions (I call them 'counts' and 'histogram') that contain the layout of the plot that I want. Both of them work when tested on individual variables. As a next step, I attempted to create a function that would take the column names, loop through a conditional to test whether the data type is an object or otherwise and call the right function based on the datatype as a new subplot. Here is the code that I have so far:
Creates list of coordinates we will need for subplot specification:
A = np.arange(21)
B = np.arange(4)
coords = []
for i in A:
for j in B:
coords.append([A[i], B[j]])
#Create the gridspec and layout the figure
import matplotlib.gridspec as gridspec
fig = plt.figure(figsize=(12,6))
gs = gridspec.GridSpec(2,4)
#Function that relies on what we've done above:
def grid(cols=['MSZoning', 'LotFrontage', 'LotArea', 'Street', 'Alley']):
for i in cols:
for vals in coords:
if str(train[i].dtype) == 'object':
plt.subplot('gs'+str(vals))
counts(cols)
else:
plt.subplot('gs'+str(vals))
histogram(cols)
When attempted, this code returns an error:
ValueError: Single argument to subplot must be a 3-digit integer
For purposes of helping you visualize, what I am hoping to achieve, I attach the screen shot below, which was produced by the line by line coding (with my created helper functions) I am trying to avoid:
Can anyone help me figure out where I am going wrong? I would appreciate any advice. Thank you!

The line plt.subplot('gs'+str(vals)) cannot work; which is also what the error tells you.
As can be seen from the matplotlib GridSpec tutorial, it needs to be
ax = plt.subplot(gs[0, 0])
So in your case you may use the values from the list as
ax = plt.subplot(gs[vals[0], vals[1]])
Mind that you also need to make sure that the coords list must have the n*m elements, if the gridspec is defined as gs = gridspec.GridSpec(n,m).

Related

Sorting algorithm visulizer: how to highlight the current element being accessed and compared in the algorithm?

So im trying to write a sorting algorithm visualizer. Code bellow. I am basically using matplotlib to plot the figure. My problem is that i want to also highlight the current element in the array being accessed, compared, and swaped. all of my attempts have failed at this. Please do also let me know if there is a better way of writing a visulizer in python. I have seen some tutorials using pygame but wanted to stick to basics. Also when the program runs till the end and everthing is sorted the plot goes blank. Is this because of the plt.clf() command and is there a way for the sorted plot to not close. Thanks!!!
from matplotlib import pyplot as plt
import numpy as np
# generate sudo-random list of numbers
lst = np.random.randint(0, 100, 20)
# x values for the bar plot
x = range(0, len(lst))
def insertion_sort(lst):
# loop through the list
# incrementally check which index to the left should i be placed in
for i in range(1, len(lst)):
while lst[i-1] > lst[i] and i>0:
lst[i], lst[i-1] = lst[i-1], lst[i]
i = i-1
# plot
plt.bar(x,lst)
plt.pause(0.1)
plt.clf()
plt.show()
return lst
print(lst)
print(insertion_sort(lst))
So the solution i came up with for this problem was to create a second list containing the current i and i-1 indexes and basically plot a second barchart over the main one set to a different color. Bad solution and failed indeed. Another idea i tried was to pass a conditional argument for the color paramater of plt.bar()
colors = ['red' if lst[i-1]>lst[i] else for element in lst 'blue']
plt.bar(x, lst, color=colors)
This did not work aswell. dont know if am on the right track and just need to keep at it or this is whole setup is futile to begin with. thank you for your time!!

How to add traces in plotly.express

I am very new to python and plotly.express, and I find it very confusing...
I am trying to use the principle of adding different traces to my figure, using example code shown here https://plotly.com/python/line-charts/, Line Plot Modes, #Create traces.
BUT I get my data from a .CSV file.
import plotly.express as px
import plotly as plotly
import plotly.graph_objs as go
import pandas as pd
data = pd.read_csv(r"C:\Users\x.csv")
fig = px.scatter(data, x="Time", y="OD", color="C-source", size="C:A 1 ratio")
fig = px.line(data, x="Time", y="OD", color="C-source")
fig.show()
The above lines produces scatter/line plots with the correct data, but the data is mixed together. I have data from 2 different sources marked by a column named "Strain" in my .csv file that I would like the chart to reflect.
Is the traces option a possible way to do it, or is there another way?
You can add traces using an Express plot by using .select_traces(). Something like:
fig.add_traces(
list(px.line(...).select_traces())
)
Note the need to convert to list, since .select_traces() returns a generator.
It looks like you probably want the lines with the scatter dots as well on a single plot?
You're setting fig to equal px.scatter() and then setting (changing) it to equal px.line(). When set to line, the scatter plot is overwritten.
You're already importing graph objects so you can use add_trace with go, something like this:
fig.add_trace(go.Scatter(x=data["Time"], y=data["OD"], mode='markers', marker=dict(color=data["C-source"], size=data["C:A 1 ratio"])))
Depending on how your data is set up, you may need to add each C-source separately doing something like:
x=data.query("C-source=='Term'")["Time"], ... , name='Term'`
Here's a few references with examples and options you can use to set up your scatter:
Scatter plot examples  
Marker styles  
Scatter arguments and attributes
You can use the apporach stated in Plotly: How to combine scatter and line plots using Plotly Express?
fig3 = go.Figure(data=fig1.data + fig2.data)
or a more convenient and scalable approach:
fig1.data and fig2.data are common tuples that hold all the info needed for a plot and the + just concatenates them.
# this will hold all figures until they are combined
all_figures = []
# data_collection: dictionary with Pandas dataframes
for df_label in data_collection:
df = data_collection[df_label]
fig = px.line(df, x='Date', y=['Value'])
all_figures.append(fig)
import operator
import functools
# now you can concatenate all the data tuples
# by using the programmatic add operator
fig3 = go.Figure(data=functools.reduce(operator.add, [_.data for _ in all_figures]))
fig3.show()
thanks for taking the time to help me out. I ended up with two solutions that worked, of which using "facet_col" to divide the plot into two subplots (1 for each strain) was the most simple solution.
https://plotly.com/python/axes/
Thanks. this worked for me also where Fig_Set_B is a list of scatter plots
# create a tuple of first line plots in first 6 plots from plot set Fig_Set_B`
fig_combined = go.Figure(data= tuple(Fig_Set_B[x].data[0] for x in range(6)) )
fig_combined.show()

Using RGB values control individual data points matplotlib

I'm trying to be able to control the colour of an individual data point using a corresponding rgb tuple. I've tried looping through the data set and plotting individual data points however I get the same effect as the code I have below; all that happens is it refuses to produce a graph.
This is an example of the data type I'm working with
Any tips?
import matplotlib.pyplot as plt
y=[(0.200,0.1100,0.520)]
for i in range(4):
y.append(y)
plt.plot([1,2,3,4], [3,4,5,2],c=y)
plt.show()
One problem is that you are appending the list to the new list. Instead, try appending the tuple to the list. Moreover, you need to use scatter plot for the color argument which contains rgb tuple for each point. However, in oyur case, I see only a single color for all the scatter points.
tup=(0.200,0.1100,0.520)
y = []
for i in range(4):
y.append(tup)
plt.scatter([1,2,3,4], [3,4,5,2], c=y)
A rather short version to your code is using a list comprehension
tup=(0.200,0.1100,0.520)
y = [tup for _ in range(4)]
plt.scatter([1,2,3,4], [3,4,5,2], c=y)

Concatenating multiple barplots in seaborn

My data-frame contains the following column headers: subject, Group, MASQ_GDA, MASQ_AA, MASQ_GDD, MASQ_AD
I was successfully able to plot one of them using a bar plot with the following specifications:
bar_plot = sns.barplot(x="Group", y='MASQ_GDA', units="subject", ci = 68, hue="Group", data=demo_masq)
However, I am attempting to create several of such bar plot side by side. Might anyone know how I can accomplish this, for each plot to contain the remaining 3 variables (MASQ_AA, MASQ_GDD, MASQ_AD). Here is an example of what I am trying to achieve.
If you look in the documentation for sns.barplot(), you will see that the function accepts a parameter ax= allowing you to tell seaborn which Axes object to use to plot the result
ax : matplotlib Axes, optional
Axes object to draw the plot onto, otherwise uses the current Axes.
Therefore, the simple way to obtain the desired output is to create the Axes beforehand, and then calling sns.barplot() with the corresponding ax parameter
fig, axs = plt.subplots(1,4) # create 4 subplots on 1 row
for ax,col in zip(axs,["MASQ_GDA", "MASQ_AA", "MASQ_GDD", "MASQ_AD"]):
sns.barplot(x="Group", y=col, units="subject", ci = 68, hue="Group", data=demo_masq, ax=ax) # <- notice ax= argument
Another option, and maybe an option that is more in line with the philosophy of seaborn is to use a FacetGrid. This would allow you to automatically create the required number of subplots depending on the number of categories in your dataset. However, it requires to reshape your dataframe so that the content of your MASQ_* columns are on a single column, with a new column showing what category each value corresponds to.

How can I programmatically select a specific subplot in Matplotlib?

So in a figure where three vertical subplots have been added with add_subplot, how can I select let's say the middle one?
Right now I do this list comprehension:
[r[0] for r in sorted([[ax, ax.get_geometry()[2]] for ax in self.figure.get_axes()], key=itemgetter(1))]
where I can simply select the index I want, with the corresponding axes. Is there a more straightforward way of doing this?
From the matplotlib documentation:
If the figure already has a subplot with key (args, kwargs) then it will simply make that subplot current and return it.
Here's an example:
import matplotlib.pyplot as plt
fig = plt.figure()
for vplot in [1,2,3]:
ax = fig.add_subplot(3,1,vplot)
ax.plot(range(10),range(10))
ax_again = fig.add_subplot(3,1,2)
ax_again.annotate("The middle one",xy=(7,5),xytext=(7,5))
plt.show()
The middle plot is called again so that it can be annotated.
What if I set the background with my original call, do I need to set it again when I get the subplot the second time?
Yes. The arguments and keywords for the original call are used to make a unique identifier. So for the figure to generate this unique identifier again, you need to pass the same arguments (grid definition, position) and keywords again. For example:
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(2,1,1,axisbg='red')
ax.plot(range(10),range(10))
ax = fig.add_subplot(2,1,2)
ax.plot(range(10),range(10))
ax_again = fig.add_subplot(2,1,1,axisbg='red')
ax_again.annotate("The top one",xy=(7,5),xytext=(7,5))
plt.show()
What if I use ax_again.change_geometry() ?
You would think change_geometry, e.g. from a 312 to a 422, would change how you use add_subplot, but it doesn't. There appears to be a bug or undefined behavior when you call change_geometry. The unique key that was original generated using the arguments and keywords, to the first add_subplot call, does not get updated. Therefore, if you want to get an axis back with an add_subplot call, you need to call add_subplot with the original arguments and keywords. For more info, follow this issue report:
https://github.com/matplotlib/matplotlib/issues/429
My guess for now is that if you change any property of the subplot after generating it with add_subplot call, the unique will not be adjusted. So just use the original arguments and keywords, and hopefully this will work out.

Resources