Im following this guide to create the plot: https://www.python-graph-gallery.com/web-circular-lollipop-plot-with-matplotlib
# Create a data frame with the information for the four words that are going to be labeled
LABELS_DF = word_count_df[word_count_df['rank'] <=4].reset_index()
#create labels
LABELS_DF['label'] = [
f"{word}\nCount: {int(count)}"
for word, count in zip(LABELS_DF['word'], LABELS_DF['count'])
]
# set position of the labels
LABELS_DF["x"] = [40, 332, 343, 342, 341]
LABELS_DF["y"] = [20, 20, 20, 20, 20]
#initialize the layout in polar coordinates
fig, ax = plt.subplots(figsize=(12,12), subplot_kw={"projection": "polar"})
# Set background color to white, both axis and figure.
fig.patch.set_facecolor("white")
ax.set_facecolor("white")
# Use logarithmic scale for the radial axis
ax.set_rscale('symlog')
# Angular axis starts at 90 degrees, not at 0
ax.set_theta_offset(np.pi / 2)
# Reverse the direction to go counter-clockwise.
ax.set_theta_direction(-1)
# Add lines
ax.vlines(ANGLES, 0 + PLUS, HEIGHTS + PLUS, color=COLORS, lw=0.9)
# Add dots
ax.scatter(ANGLES, HEIGHTS + PLUS, s=scale_to_interval(HEIGHTS), color=COLORS)
# remove spines for both axes
ax.spines["start"].set_color('none')
ax.spines["polar"].set_color("none")
#remove gridlines, ticks, tick labels
ax.grid(False)
ax.set_xticks([])
ax.set_yticklabels([])
# Add our custom grid lines for the radial axis.
# These lines indicate 1, 2, 4, 8 instances of the word
HANGLES = np.linspace(0, 2 * np.pi, 200)
ax.plot(HANGLES, np.repeat(1 + PLUS, 200), color= GREY88, lw=0.7)
ax.plot(HANGLES, np.repeat(2 + PLUS, 200), color= GREY85, lw=0.7)
ax.plot(HANGLES, np.repeat(4 + PLUS, 200), color= GREY82, lw=0.7)
ax.plot(HANGLES, np.repeat(8 + PLUS, 200), color= GREY79, lw=0.7)
# If you have a look at the beginning of this post, you'll see the inner circle is not white.
# This fill creates the effect of a very light grey background.
ax.fill(HANGLES, np.repeat(PLUS, 200), GREY97)
# Note the 'transform=ax.transAxes'
# It allows us to pass 'x' and 'y' in terms of the (0, 1) coordinates of the axis
# instead of having to use the coordinates of the data.
# (0.5, 0.5) represents the middle of the axis in this transformed coordinate system
ax.text(
x=0.5, y=0.508, s="********\nA Highly\nScientific Analysis\n of \nChinese Restaurant Names\n********",
color=GREY60, va="center", ha="center", ma="center", fontfamily="Gabriola",
fontsize=27, fontweight="bold", linespacing=1.1, transform=ax.transAxes)
# Add labels for the five selected words
for idx, row in LABELS_DF.iterrows():
color = COLORS[row["index"]]
ax.text(
x=ANGLES[row["x"]], y=row["y"], s=row["label"], color=color,
ha="right", va="center", ma="center", size=8,
family="Gabriola", weight="bold"
)
def circular_plot(angles, heights, colors, lw, ax):
ax.set_facecolor("white")
ax.set_rscale("symlog")
ax.set_theta_offset(np.pi / 2)
ax.set_theta_direction(-1)
ax.spines['start'].set_color('none')
ax.spines['start'].set_color('none')
#remove gridlines ticks and tick labels
ax.grid(False)
ax.set_xticks([])
ax.set_yticklabels([])
# The 'lw' argument controls the width of the lines.
# This is going to be different for the top and lower panels.
ax.vlines(angles, 0 + PLUS, heights + PLUS, color=colors, lw=lw)
ax.scatter(angles, heights + PLUS, s=scale_to_interval(heights), color=colors)
HANGLES = np.linspace(0, 2 * np.pi, 200)
ax.plot(HANGLES, np.repeat(1 + PLUS, 200), color= GREY88, lw=0.7)
ax.plot(HANGLES, np.repeat(2 + PLUS, 200), color= GREY85, lw=0.7)
ax.plot(HANGLES, np.repeat(4 + PLUS, 200), color= GREY82, lw=0.7)
ax.plot(HANGLES, np.repeat(8 + PLUS, 200), color= GREY79, lw=0.7)
ax.fill(HANGLES, np.repeat(PLUS, 200), GREY97)
#Change upper limit of the radial axis so larger dots fit within the plot area
ax.set_rmax(ax.get_rmax() * 2)
def map_category(category):
if category == "other":
return "Other"
elif category == "family_name":
return "Family\nNames"
elif category == "place":
return "Places"
elif category == "food":
return "Food"
elif category == "direction":
return "Direction"
elif category == 'old/new':
return 'Old\nand\nNew'
elif category == 'sun/moon':
return 'Sun and Moon'
elif category == 'precious_metal':
return 'Precious\nMetals'
elif category == 'animal':
return 'Animals'
elif category == 'royalty':
return 'Royalty'
elif category == 'provinces':
return ' Chinese\nProvinces'
elif category == 'taste':
return 'Tastes'
else:
return category
CATEGORIES = sorted(pd.Categorical(word_count_df["category"]).unique())
LABELS = [map_category(category) for category in CATEGORIES]
# The plot consists of 2 rows and 5 columns (10 categories in total)
fig, axes = plt.subplots(2, 5, figsize=(15, 6), subplot_kw={"projection": "polar"})
fig.patch.set_facecolor("white")
# Define the slices used to iterate through 'axes'.
# It iterates in a rowwise manner.
# It starts in the first row, and iterates over all the columns of that row
# from left to right, then it goes to the next row and does the same.
SLICES = [(i, j) for i in range(2) for j in range(5)]
for category, label, slice_ in zip(CATEGORIES, LABELS, SLICES):
# Select axis
ax = axes[slice_]
# Select indexes corresponding to the passwords in this category
idx = word_count_df.index[word_count_df["category"] == category].tolist()
# Subset ANGLES, HEIGHTS, and COLORS to use the ones for this category.
angles = ANGLES[idx]
heights = HEIGHTS[idx]
colors = COLORS[idx]
# Create circular plot
circular_plot(angles, heights, colors, 0.8, ax)
# Add text within the inner circle representing the category
ax.text(
x=0.5, y=0.5, s=label, color=colors[0], va="center", ha="center",
ma="center", fontfamily="Gabriola", fontsize=16, fontweight="bold",
linespacing=0.87, transform=ax.transAxes
)
# Adjust space between subplots.
# 'wspace=0' leaves no horizontal space between subplots.
# 'hspace=0' leaves no vertical space between subplots.
fig.subplots_adjust(wspace=0.1, hspace=0.1)
I'm trying to remove the dark ring on the out portion of the subgraphs in the second output image so that all 10 graphs look like the graph from the first image. I tried changing the colors in HANGLES variable, but it doesn't change the very outer ring's color. I'm also having trouble understanding what the y values for the y-axis coordinates of my label should be to match up with the coordinating lollipops in the graph.
Related
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
I am plotting several lines in the same figure (which is a football pitch) like so:
fig, ax = create_pitch(120, 80,'white')
for index, pass_ in cluster5.iterrows():
if (index < 0):
continue
x, y = pass_['x'], pass_['y']
end_x, end_y = pass_['end_x'], pass_['end_y']
y = 80 - y
end_y = 80 - end_y
color = color_map[pass_['possession']]
ax.plot([x, end_x], [y, end_y], linewidth = 3, alpha = 0.75, color = color, label = pass_['possession'])
ax.legend(loc = 'upper left')
There are several groups and I would like to plot a single legend for them.
However, I now have a legend of repeated items (one for each call to ax plot for each label).
How can I just plot a single legend item for each label?
Thanks a lot in advance!
I solved this by adding a proxy plot and handle:
for c in color_labels:
ax.plot(x, y, linewidth = 3, color = color_map[c], alpha = 0.75, label = c)
with x, y being the last used one. such that the final color is the same.
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
I want to create a before-and-after plot of the three axis of my measurement system. This is close to what I want. However:
How do I have the "before" and "after" titles span subplot 1+2 and 4+5, respectively? (the tabs dont work as expected)
Like "before" and "after" should be above a column, i would like to have the "x-Axis", "y-Axis" etc infront of the row of graphs. How do I do that?
How do I join subplot 1+2 and 4+5 together, so that they touch? wspace=.0 doesnt work as expected.
How do I reduce the width in the middle, where subplot 3 would be, so that the other sides can take up more space?
How do I add some hspace between the fig.suptitle and the graphs?
How can I make my code more elegant? There is a lot of repetition.
from matplotlib.pyplot import figure
def plot_before_and_after(data_before, data_after, title):
shape = data_before.shape
sensor_num = shape[0]
n_start = 20 # number picked at random
N = 2 ** 12 # power of two is good
n_stop = n_start + N
p_stop = n_start + 40 # one periode #50Hz at the sampling rate
x_long = range(n_start, n_stop)
x_short = range(n_start, p_stop)
cmap = plt.get_cmap('jet_r')
axis_list = ['x', 'y', 'z']
fig = figure(num=None, figsize=(10, 10), dpi=80, facecolor='w', edgecolor='k')
fig.suptitle(title + "\nbefore \t\t\t\t\tafter")
plots = []
for axis_cnt in range(0, 3):
ax0 = plt.subplot(3, 5, axis_cnt * 5 + 1,
title="before, {}-Axis".format(axis_list[axis_cnt]))
for sensor_cnt in range(0, sensor_num):
color = cmap(float(sensor_cnt) / sensor_num)
plt.plot(x_long, data_before[sensor_cnt, n_start:n_stop, axis_cnt], color=color,
label="sensor" + str(sensor_cnt))
ax1 = plt.subplot(3, 5, axis_cnt * 5 + 2,
title="before, {}-Axis".format(axis_list[axis_cnt]),
sharey=ax0)
for sensor_cnt in range(0, sensor_num):
color = cmap(float(sensor_cnt) / sensor_num)
plt.plot(x_short, data_before[sensor_cnt, n_start:p_stop, axis_cnt], color=color,
label="sensor" + str(sensor_cnt))
plt.setp(ax1.get_yticklabels(), visible=False)
ax3 = plt.subplot(3, 5, axis_cnt * 5 + 4,
title="after, {}-Axis".format(axis_list[axis_cnt]))
for sensor_cnt in range(0, sensor_num):
color = cmap(float(sensor_cnt) / sensor_num)
plt.plot(x_long, data_after[sensor_cnt, n_start:n_stop, axis_cnt], color=color,
label="sensor" + str(sensor_cnt))
ax4 = plt.subplot(3, 5, axis_cnt * 5 + 5,
title="after, {}-Axis".format(axis_list[axis_cnt]),sharey=ax3)
for sensor_cnt in range(0, sensor_num):
color = cmap(float(sensor_cnt) / sensor_num)
plt.plot(x_short, data_after[sensor_cnt, n_start:p_stop, axis_cnt], color=color,
label="sensor" + str(sensor_cnt))
plt.setp(ax4.get_yticklabels(), visible=False)
plt.subplots_adjust(wspace=.0)
plt.legend()
plt.show()
Here's a preliminary answer that may help you. I used Matplotlib's GridSpec (see here for useful information) and the add_subplot method, both of which seem to be convenient in these cases. The gridspec is what allows us to create the two groups of subplots independently formatted; the add_subplot generates the individual axes.
Code
import matplotlib.pyplot as plt
nrow, ncol = 3, 2 # Number of rows and cols of gridspecs
lborder = [0.1, 0.6] # Left border coordinates of gridspecs
rborder = [0.45, 0.95] # Right border coordinates of gridspecs
tborder = 0.92 # Top border coordinate of gridspecs
gtitles = ['Before', 'After']
txt_axis = ['X-axis', 'Y-axis', 'Z-axis']
fig = plt.figure(figsize=(10, 10), dpi=80, facecolor='w', edgecolor='k')
for i in range(2):
gs = fig.add_gridspec(nrows=nrow, ncols=ncol, left=lborder[i],
right=rborder[i], top=tborder, hspace=0.45, wspace=0)
for j in range(nrow):
ax0 = fig.add_subplot(gs[j,0])
ax0.plot([1,2,3])
plt.text(-0.4, 0.5, txt_axis[j],
horizontalalignment='center',verticalalignment='center',
transform = ax0.transAxes, fontsize = 12)
if j == 0:
fig.text(1, 1.1, gtitles[i], fontsize=12, horizontalalignment =
'center', transform = ax0.transAxes)
for k in range(1, ncol):
ax1 = fig.add_subplot(gs[j,k], sharey = ax0)
plt.setp(ax1.get_yticklabels(), visible=False)
ax1.plot([1,2,3])
fig.suptitle('Figure title', fontsize = 14)
As for your questions:
I created the 'Before' and 'After' titles using text, based on this answer).
Same thing for the "-axis" text. Note that it will probably overlap with any axes label you write on the vertical axis. Also note that now we have to shift the left gridspec slightly to the right (via the leftargument of add_gridspec).
wspace can be introduced in add_gridspec too. I don't know why it doesn't work in your code.
For the space in between the 2 gridspecs, use the left and right arguments in the add_gridspec function.
The space between the main title and the subplots can be achieved via the top argument in add_gridspec.
Your inner loops seem very similar, perhaps you could define a function and save some lines of code. In my case, I tried to encase everything in a loop.
Hope it helps.
I tried with the package epade but I failed!
Example:
Each one of the x values defines the height of each bar (bars as many x values exist, with x height).
xa<-c(9.45,6.79,14.03,7.25,16.16,19.42,16.30,4.60,14.76,19.24,
16.04,7.80,13.16,10.00,15.76,16.29,19.52,27.22,7.74,6.75)
barplot(xa)
So I would like exactly the same plot in 3d looking fashion!
Is it possible?
UPDATED SOLUTION
This was done in Python, not in R :(
Here is the code:
# needed modules
import csv
import pandas as pandas
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
from scipy.interpolate import spline
from textwrap import wrap
from mpl_toolkits.mplot3d import proj3d
import pylab
import os
# we define some names in order to change only one
# 3 columnes are imported each time
# by changing col_inc from 0 to something
# we can define which range of columns will be imported
col_num = np.arange(2, 1001)
col_num_tuple = tuple(col_num)
cnt = col_num_tuple
cnt
# last counter col_inc = 279
col_inc = 273
str = 0 + col_inc
fin = 3 + col_inc
cols = cnt[str:fin]
cols
# importing a simple datafile, csv type. Data are comma seperated
# importing only 1st, 2nd and 4th columns
# We can call these data now by giving a new name, 'io'.
io = pandas.read_csv(
'/data.csv', sep=",", usecols=cols)
# Try to get the name of singer & the name of song
# in the first two rows
names = io[0:2]
nm = names
nm1 = np.array(nm[:], dtype='string')
nm_singer = nm1[0:1][0:2, 1][0]
nm_song = nm1[1:2][0:2, 1][0]
nm_singer
nm_song
nms = nm_singer + ' - ' + nm_song
# we drop nan values
io = io.dropna()
# we make this in order not change each time, the name of column
io_all = np.array(io[3:])
io_st = np.array(io_all[:, 0], dtype=float)
io_end = np.array(io_all[:, 1], dtype=float)
io_dur = np.array(io_all[:, 2], dtype=float)
io_all
io_st
io_end
io_dur
# We define a new name for the column that is named alice inside io dataset
result = io_dur
# we need to make these data 'array type'
result = np.array(result)
# we now define the dimensions of our figure/plot, as well its dpi
fig = plt.figure(figsize=(16, 8), dpi=150)
# This line defines our first plot
# Afterwards, the '112' will define our second plot.
ax1 = fig.add_subplot(111, projection='3d')
# ax1 = Axes3D(fig)
# we define here labels
xlabels = io_end
xpos = np.arange(xlabels.shape[0])
ylabels = np.array([''])
ypos = np.arange(ylabels.shape[0])
xposM, yposM = np.meshgrid(xpos, ypos, copy=False)
zpos = result
zpos = zpos.ravel()
# this defines the dimensions of the actual boxes
# you can play with these values.
dx = 0.7
dy = 0.7
dz = zpos
# here, we define ticks, they are placed in the 'middle' of each bar
ax1.w_xaxis.set_ticks(xpos + dx / 2.)
ax1.w_xaxis.set_ticklabels(xlabels, rotation='vertical')
ax1.w_yaxis.set_ticks(ypos + dy / 2.)
ax1.w_yaxis.set_ticklabels(ylabels)
# here we define the colors of the bars, rainbow style
# you can play with these numbers
values = np.linspace(0.2, 1., xposM.ravel().shape[0])
colors = cm.rainbow(values)
# figure subtitle
# fig.suptitle('test title', fontsize=20)
# here, we define in the x axis the size of its ticks, its numbers
ax1.tick_params(axis='x', which='major', pad=0, labelsize=7)
# Here, we define the limits of y axis,
# NOTE that this defines WHERE bars will be placed
# IN relation to the rest figure,
# their offset point
plt.ylim((-2, 5))
# this says if the grid will be printed
plt.grid(True)
# this defines the placement of the 3d plot in its placeholders,
# in the surrounding white space
# I was surprised! The below line is not needed at all!
# fig.subplots_adjust(left=0, right=0, bottom=0, top=0)
# this is the actual command to define the plot
# all 6 parameters that we previously defined, are placed here.
# colors is an extra parameter
ax1.bar3d(xposM.ravel(), yposM.ravel(), dz * 0, dx, dy, dz, color=colors)
# elevation and azimuth, basically, definition of the view angle
ax1.view_init(0, -95)
# here we define that we will place a second plot
# Neither this line is needed!
# ax1 = fig.add_subplot(112, projection='3d')
# To produce numbers from 0 according to how many data exist in 'result'
x = np.arange(0, len(result))
y = result
# I try to center the line in relation to the top of bars.
y += 5
# Produce more points in order to make the line to look nicer (300).
x_smooth = np.linspace(x.min(), x.max(), 300)
y_smooth = spline(x, y, x_smooth)
# smooth line sometimes went below zero in some extreme cases.
# Therefore I added this if statement to find these cases
# and increase the height of the smooth line so much points
# as the points that went below 0
if min(y_smooth) <= 0:
y -= (min(y_smooth))-1
y_smooth = spline(x, y, x_smooth)
# a trick to center the line to bars
x_smooth += 0.4
# here,i try to produce a 'z' array of so many zeros as the length
# of 'x_smooth line'
z = np.linspace(0, 0, len(x_smooth))
# here, we define the parameters of the second plot.
# ax1' symbol is duplicated
# in order to plot the line in the same plot with the barplot.
ax1.plot(x_smooth, z, y_smooth)
# this try to align the y title
ax1.annotate(
'\n'.join(wrap('Duration of each Rythm (in sec)', 20)),
xy=(0.20, 0.80), xytext=(0, 0), fontsize=8, color='steelblue',
style='italic',
xycoords='axes fraction', textcoords='offset points',
bbox=dict(facecolor='mistyrose', alpha=0.3),
horizontalalignment='center', verticalalignment='down')
# this try to align the x title
ax1.annotate(
'\n'.join(wrap('Where Rythm is broken (in sec)', 20)),
xy=(0.27, 0.06), xytext=(0, 0), fontsize=9, color='steelblue',
xycoords='axes fraction', textcoords='offset points',
bbox=dict(facecolor='peachpuff', alpha=0.3),
horizontalalignment='center', verticalalignment='down')
# this try to center the bottom title
ax1.annotate(
'\n'.join(wrap(nms, 100)), xy=(0.5, 0.07),
xytext=(0, 0), fontsize=11,
xycoords='axes fraction', textcoords='offset points',
bbox=dict(facecolor='mediumorchid', alpha=0.3),
horizontalalignment='center', verticalalignment='down')
# Eedefine path and filename in order to save in custom made filename
pathnm = '/'
filenm = nms
nflnm = '%s_3D.png' % filenm
npath = os.path.join(pathnm, nflnm)
# saving our plot
#fig.savefig(npath, bbox_inches='tight', pad_inches=0,figsize=(46,15),dpi=400)
plt.show(fig)
io[0:2]'code'