Graph with loop which does not display in streamlit - python

i try to do a spyder graph for my streamlit app. However, it functions on my notebook but not in my streamlit. Is anyone has an idea of the problem it does not display anything ?
Spider_kmeans = df[['Cluster_kmeans_Label','Age','Annual Income (k$)','Spending Score (1-100)','Sex (100=Male)']]
Spider_kmeans = Spider_kmeans.groupby('Cluster_kmeans_Label')['Cluster_kmeans_Label','Age','Annual Income (k$)','Spending Score (1-100)','Sex (100=Male)'].mean().reset_index()
def make_spider_kmeans( row, title, color):
# number of variable
categories=list(Spider_kmeans)[1:]
N = len(categories)
# What will be the angle of each axis in the plot? (we divide the plot / number of variable)
angles = [n / float(N) * 2 * pi for n in range(N)]
angles += angles[:1]
# Initialise the spider plot
ax = plt.subplot(3,3,row+1, polar=True)
# If you want the first axis to be on top:
ax.set_theta_offset(pi / 2)
ax.set_theta_direction(-1)
# Draw one axe per variable + add labels labels yet
plt.xticks(angles[:-1], categories, color='black',fontfamily='serif',fontweight='light', size=8)
#ax.set_xticks([]) # turn labels off if you want - can look quite nice
# Draw ylabels
ax.set_rlabel_position(0)
plt.yticks([10,20,30,40,50,75,100], ["10","20","30","40","50","75","100"], color="grey", size=4)
plt.ylim(0,100)
# Ind1
values= Spider_kmeans.loc[row].drop('Cluster_kmeans_Label').values.flatten().tolist()
values += values[:1]
ax.plot(angles, values, color=color, linewidth=2, linestyle='solid')
ax.fill(angles, values, color=color, alpha=0.4)
# Add a title
plt.title(title, size=10, fontfamily='serif',fontweight='bold', y=1.2)
plt.tight_layout()
#############################################################################################
my_dpi=96
plt.figure(figsize=(1000/my_dpi, 1000/my_dpi), dpi=my_dpi)
# Create a color palette:
my_palette = plt.cm.get_cmap("crest", len(df.index))
then i put this code on my streamlit app
for row in range(0, len(Spider_kmeans.index)):
make_spider_kmeans( row=row, title='Cluster: '+ Spider_kmeans['Cluster_kmeans_Label'][row], color='#244747') #change this to my_palette if you want colour to vary by cluster
I

Related

Colorbar line plot of multiple lines according to a third variable value

Dataset: I have a series (n = 30) of X (wavelength) and Y (reflectance) data, each associated with a unique value Z (age). Z values are stored as a separate ordered list.
Goal: I am trying to create a series of line plots which display each of the 30 datasets together, where each line is appropriately colored according their Z value (age). I am hoping for weighted colorization depending on the Z value, and an associated colorbar() or similar.
Attempts: I tried manipulating rcParams to do this by iterating through a color-scheme per plot [i], but the colors are not weighted properly to the Z value. See example figure. I think my issue is similar to this question here.
I feel like this shouldn't be so hard and that I am missing something obvious!
#plot
target_x = nm_names
target_y = data_plot
target_names = ages
N = len(target_y) # number of objects to plot i.e. color cycle count
plt.rcParams["figure.figsize"] = [16,7] # fig size
plt.rcParams["axes.prop_cycle"] = plt.cycler("color", plt.cm.PiYG(np.linspace(0,1,N))) # colors to cycle through, choose default like 'viridis' or 'PiYG'
fig, ax = plt.subplots()
for i in range(N):
ax.plot(target_x, target_y.iloc[i], label = target_names[i]) # for i in range of objects, plot x,y
#axes
plt.xticks(fontsize = 10, rotation=70, size = 8)
ax.xaxis.set_major_locator(ticker.MultipleLocator(50))
plt.xlabel('Wavelength (nm)', fontsize = 14)
plt.yticks(fontsize = 12)
plt.ylabel('Normalized Relative Reflectance', fontsize = 13)
plt.title("Spectral Profile", size = 14)
plt.title
plt.xlim(375,2500)
# legend location
box = ax.get_position()
ax.set_position([box.x0, box.y0 + box.height * 0.1,
box.width, box.height * .9])
ax.legend(loc='lower left', bbox_to_anchor=(1, 0),
fancybox=True, shadow=True, ncol=1, title = 'Age (ky)') # Put a legend below current axis
plt.rcdefaults() # reset global plt parameters, IMPORTANT!
plt.show()
My plot, where 'age' is the 'Z' value

How to add values at the end of Radar Chart points?

so I am plotting a Radar Chart and I need to add values at the end of each point of the chart. Is there a way? Main area of code and df enlisted below.
"""PLOT GRAPH 1"""
categories=list(df)[0:]
N = len(categories)
categories=list(df)[0:]
N = len(categories)
values=df.iloc[1].values.flatten().tolist()
values += values[:1]
for_tick=df.iloc[0].values.flatten().tolist()
for_tick += for_tick[:1]
angles = [n / float(N) * 2 * pi for n in range(N)]
angles += angles[:1]
f, ax = plt.subplots(1,1,figsize=(8,6))
ax = plt.subplot(111, polar=True)
plt.xticks(angles[:-1], wrapped_labels , size=10)
ax.set_rlabel_position(0)
plt.yticks([0,25,50,75,100], color="grey", size=5)
plt.ylim(0,100)
plt.title('Suppliers hit in the test out of all supliers (' +number_of_suppliers+') (in %)')
ax.plot(angles, values, color='#ffe600', linewidth=1, linestyle='solid')
ax.fill(angles, values, color='#ffe600', alpha=0.3)
plt.savefig(r'C:\Radar\firmy.png',dpi=100)
plt.show()
So I managed to find a more manual way, as there were only five values to label, of how to do it, adding this line of code. If I find more intuitive and automatic way, I will update you :)
ax.annotate(labels[0], xy=(angles[0],values[0]), textcoords='data',size=5,ha="center", va="center")
ax.annotate(labels[1], xy=(angles[1],values[1]), textcoords='data',size=5,ha="center", va="center")
ax.annotate(labels[2], xy=(angles[2],values[2]), textcoords='data',size=5,ha="center", va="center")
ax.annotate(labels[3], xy=(angles[3],values[3]), textcoords='data',size=5,ha="center", va="center")
ax.annotate(labels[4], xy=(angles[4],values[4]), textcoords='data',size=5,ha="center", va="center")

3D Plot of Multiple Time Series in Python

I've seen numerous examples of 3D plots using matplotlib/seaborn in Python but can't seem to get what I'm looking for; I have 50 or so timeseries that I would like to plot cleanly as in the following example below but with the name of the series on the axis; as an example I've marked in Goog, IBM, GE, Pepsi etc. Appreciate any pointers or examples. Thank you,
Example PLOT Click Here Please
Matplotlib has very rich gallery. I found this, you can only plot it once instead of animation. And manually put y-axis legend wherever you want.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
# Fixing random state for reproducibility
np.random.seed(19680801)
# Create new Figure with black background
fig = plt.figure(figsize=(12, 8))
# Add a subplot with no frame
ax = plt.subplot(111, frameon=False)
# Generate random data
data = np.random.uniform(0, 1, (64, 75))
X = np.linspace(-1, 1, data.shape[-1])
G = 1.5 * np.exp(-4 * X ** 2)
# Generate line plots
lines = []
for i in range(len(data)):
# Small reduction of the X extents to get a cheap perspective effect
xscale = 1 - i / 200.
# Same for linewidth (thicker strokes on bottom)
lw = 1.5 - i / 100.0
line, = ax.plot(xscale * X, i + G * data[i], color="b", lw=lw)
lines.append(line)
# Set y limit (or first line is cropped because of thickness)
ax.set_ylim(-1, 70)
# No ticks
ax.set_xticks([])
ax.set_yticks([])
# 2 part titles to get different font weights
ax.text(0.5, 1.0, "MATPLOTLIB ", transform=ax.transAxes,
ha="right", va="bottom", color="k",
family="sans-serif", fontweight="light", fontsize=16)
ax.text(0.5, 1.0, "UNCHAINED", transform=ax.transAxes,
ha="left", va="bottom", color="k",
family="sans-serif", fontweight="bold", fontsize=16)
def update(*args):
# Shift all data to the right
data[:, 1:] = data[:, :-1]
# Fill-in new values
data[:, 0] = np.random.uniform(0, 1, len(data))
# Update data
for i in range(len(data)):
lines[i].set_ydata(i + G * data[i])
# Return modified artists
return lines
# Construct the animation, using the update function as the animation director.
anim = animation.FuncAnimation(fig, update, interval=10)
plt.show()

How to label the vertical lines independent of the scale of the plot?

My program takes n sets of data and plots their histograms.
I. How to label the vertical lines independent of the height of the plot?
A vertical line indicates the most frequent value in a dataset. I want to add a label indicating the value, say 20% from the top. When using matplotlib.pyplot.text() I had to manually assign x and y values. Depending up on the dataset the text goes way up or way down which I don't want to happen.
matplot.axvline(most_common_number, linewidth=0.5, color='black')
matplot.text(most_common_number + 3, 10, str(most_common_number),
horizontalalignment='center', fontweight='bold', color='black')
I also tried setting the label parameter of matplotlib.pyplot.axvline() but it only adds to the legend of the plot.
matplot.axvline(most_common_number, linewidth=0.5, color='black', label=str(most_common_number))
I wonder if there is a way to use percentages so the text appears n% from the top or use a different method to label the vertical lines. Or am I doing this all wrong?
II. How to make the ticks on x-axis to be spaced out better on resulting image?
I want the x-axis ticks to be factors of 16 so I had to override the defaults. This is where the trouble began. When I save the plot to a PNG file, the x-axis looks really messed up.
But when I use show() it works fine:
Program Snippet
kwargs = dict(alpha=0.5, bins=37, range=(0, 304), density=False, stacked=True)
fig, ax1 = matplot.subplots()
colors = ['tab:blue', 'tab:orange', 'tab:green', 'tab:red', 'tab:purple', 'tab:brown', 'tab:pink', 'tab:gray', 'tab:olive', 'tab:cyan']
count = 0
'''
datasets = [('dataset name', ['data'])]
'''
for item in datasets:
dataset = item[1]
most_common_number = most_common(dataset)
ax1.hist(dataset, **kwargs, label=item[0], color=colors[count])
matplot.axvline(most_common_number, linewidth=0.5, color='black')
matplot.text(most_common_number + 3, 10, str(most_common_number),
horizontalalignment='center', fontweight='bold', color='black')
count += 1
#for x-axis
loc = matplotticker.MultipleLocator(base=16) # this locator puts ticks at regular intervals
ax1.xaxis.set_major_locator(loc)
#for y-axis
y_vals = ax1.get_yticks()
ax1.set_yticklabels(['{:3.1f}%'.format(x / len(datasets[0][1]) * 100) for x in y_vals])
#set title
matplot.gca().set(title='1 vs 2 vs 3')
#set subtitle
matplot.suptitle("This is a cool subtitle.", va="bottom", family="overpass")
matplot.legend()
fig = matplot.gcf()
fig.set_size_inches(16, 9)
matplot.savefig('out.png', format = 'png', dpi=120)
matplot.show()
I. How to label the vertical lines independent of the height of the plot?
It can be done in two ways:
Axes limits
matplotlib.pyplot.xlim and matplotlib.pyplot.ylim
ylim() will give the max and min values of the axis. eg: (0.0, 1707.3)
matplot.text(x + matplot.xlim()[1] * 0.02 , matplot.ylim()[1] * 0.8,
str(most_common_number),,
horizontalalignment='center', fontweight='bold', color='black')
(x + matplot.xlim()[1] * 0.02 means at x but 2% to the right. Because you don't want the text to coincide on the vertical line it labels.
matplot.ylim()[1] * 0.8 means at 80% height of the y-axis.
Or you can directly specify x and y as scale (eg: 0.8 of an axis) using transform parameter:
matplot.text(most_common_number, 0.8,
' ' + str(most_common_number), transform=ax1.get_xaxis_transform(),
horizontalalignment='center', fontweight='bold', color='black')
Here y = 0.8 means at 80% height of y-axis.
II. How to make the ticks on x-axis to be spaced out better on resulting image?
Use matplotlib.pyplot.gcf() to change the dimensions and use a custom dpi (otherwise the text will not scale properly) when saving the figure.
gcf() means "get current figure".
fig = matplot.gcf()
fig.set_size_inches(16, 9)
matplot.savefig('out.png', format = 'png', dpi=120)
So the resulting image will be (16*120, 9*120) or (1920, 1080) px.

How to colour circular lines in polar chart (matplotlib)

I'm trying to to colour the circular line that corresponds to the value of 0 in a polar chart. This is what I want to achieve:
On this related question (Shading a segment between two lines on polar axis (matplotlib)), ax.fill_between is used to fill the space between two values, but I'm looking for a way to colour just the circular line where the value for each variable is 0.
If anybody has any tips that would be most appreciated! I've inserted a minimal working example below if anybody fancies having a go.
import matplotlib.pyplot as plt
import pandas as pd
def make_spider(row, title, color):
import math
categories = list(df)
N = len(categories)
angles = [n / float(N) * 2 * math.pi for n in range(N)]
angles += angles[:1]
ax = plt.subplot(1, 5, row+1, polar=True)
plt.xticks(angles[:-1], categories, color='grey', size=8)
values = df.iloc[row].values.flatten().tolist()
values += values[:1]
ax.plot(angles, values, color=color, linewidth=2, linestyle='solid')
ax.fill(angles, values, color=color, alpha = .4)
plt.gca().set_rmax(.4)
my_dpi = 40
plt.figure(figsize=(1000/my_dpi, 1000/my_dpi), dpi=96)
my_palette = plt.cm.get_cmap('Set2', len(df.index)+1)
for row in range(0, len(df.index)):
make_spider( row = row, title='Cluster: ' + str(row), color=my_palette(row) )
Example dataframe here:
df = pd.DataFrame.from_dict({"no_rooms":{"0":-0.3470532925,"1":-0.082144001,"2":-0.082144001,"3":-0.3470532925,"4":-0.3470532925},"total_area":{"0":-0.1858487321,"1":-0.1685491141,"2":-0.1632483955,"3":-0.1769700284,"4":-0.0389887094},"car_park_spaces":{"0":-0.073703681,"1":-0.073703681,"2":-0.073703681,"3":-0.073703681,"4":-0.073703681},"house_price":{"0":-0.2416123064,"1":-0.2841806825,"2":-0.259622004,"3":-0.3529449824,"4":-0.3414842657},"pop_density":{"0":-0.1271390651,"1":-0.3105853643,"2":-0.2316607937,"3":-0.3297832328,"4":-0.4599021194},"business_rate":{"0":-0.1662745006,"1":-0.1426329043,"2":-0.1577528867,"3":-0.163560133,"4":-0.1099718326},"noqual_pc":{"0":-0.0251535462,"1":-0.1540641646,"2":-0.0204666924,"3":-0.0515740013,"4":-0.0445135996},"level4qual_pc":{"0":-0.0826103951,"1":-0.1777759951,"2":-0.114263357,"3":-0.1787044751,"4":-0.2709496389},"badhealth_pc":{"0":-0.105481688,"1":-0.1760349683,"2":-0.128215043,"3":-0.1560577648,"4":-0.1760349683}})
Probably a cheap hack based on the link you shared. The trick here is to simply use 360 degrees for fill_between and then use a very thin region around the circular line for 0 using margins such as -0.005 to 0.005. This way, you make sure the curve is centered around the 0 line. To make the line thicker/thinner you can increase/decrease this number. This can be straightforwardly extended to color all circular lines by putting it in a for loop.
ax.plot(angles, values, color=color, linewidth=2, linestyle='solid')
ax.fill(angles, values, color=color, alpha = .4)
ax.fill_between(np.linspace(0, 2*np.pi, 100), -0.005, 0.005, color='red', zorder=10) # <-- Added here
Other alternative could be to use a Circle patch as following
circle = plt.Circle((0, 0), 0.36, transform=ax.transData._b, fill=False, edgecolor='red', linewidth=2, zorder=10)
plt.gca().add_artist(circle)
but here I had to manually put 0.36 as the radius of the circle by playing around so as to put it exactly at the circular line for 0. If you know exactly the distance from the origin (center of the polar plot), you can put that number for exact position. At least for this case, 0.36 seems to be a good guess.
There is an easier option:
fig_radar.add_trace(go.Scatterpolar(
r = np.repeat(0, 360),
dtheta = 360,
mode = 'lines',
name = 'cirlce',
line_color = 'black',
line_shape="spline"
)
The addition of line_shape = "spline" makes it appear as a circle
dtheta divides the coordinates in so many parts (at least I understood it this way and it works)

Categories