Growing/Animated bar plot with ceiling value as y-value - python

I am looking to develop an animated/growing bar plot.The plot basically contains of 6 rectangular bars and each bar has a particular value.
The problem I'm facing is that the plot is growing up to the maximum value on Y-axis instead it should stop at the bar's corresponding value.
The code I have tried makes the bars animate up to the maximum value on Y-Axis.I have found some information from
Growing matplotlib bar charts
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
%matplotlib notebook
fig = plt.figure()
position = np.arange(6) + .5
plt.tick_params(axis = 'x', colors = '#072b57')
plt.tick_params(axis = 'y', colors = '#072b57')
speeds = [.01, .02, .03, .04, .01, .02]
heights = [0, 0, 0, 0, 0, 0]
# Bar plot should animate up to these values
# Adding this portion is making the plot static
#heights = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6]
rects = plt.bar(position, heights, align = 'center', color=['red', 'orange', 'blue', 'pink', 'green','purple'])
plt.xticks(position, ('Anger', 'Sadness', 'Disgust', 'Fear', 'Happy', 'Surprise'))
plt.xlabel('Emotion', color = '#072b57')
plt.ylabel('Probabilities', color = '#072b57')
plt.title('Emotion - Ally', color = '#072b57')
plt.ylim((0,1))
plt.xlim((0,6))
plt.grid(True)
rs = [r for r in rects]
def init():
return rs
def animate(i):
global rs, heights
if all(map(lambda x: x==1, heights)):
heights = [0, 0, 0, 0, 0, 0]
else:
heights = [min(h+s,1) for h,s in zip(heights,speeds)]
# Bar plot should animate up to these values
# Adding this portion is making the plot static
#heights = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6]
for h,r in zip(heights,rs):
r.set_height(h)
return rs
anim = animation.FuncAnimation(fig, animate, init_func=init,frames=200, interval=20, blit=True)
plt.show()
The animated bar plot should stop at designated y-values instead of hitting the maximum value on y-axis.

Right now, your bar height is derived from min(h+s,1), which means it will grow with a certain speed (or rather step size) until it hits the value of 1.
If you want to limit the height, you should create a respective array, like max_heights = [0.5, .6, 1.0, 0.6, 0.1, 1.0] and change the heights calculation within the else case to heights = [min(h+s,mh) for h,s,mh in zip(heights,speeds,max_heights)].
In summary:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
# %matplotlib notebook
fig = plt.figure()
position = np.arange(6) + .5
plt.tick_params(axis = 'x', colors = '#072b57')
plt.tick_params(axis = 'y', colors = '#072b57')
speeds = [.01, .02, .03, .04, .01, .02]
heights = [0, 0, 0, 0, 0, 0]
# Bar plot should animate up to these values
# Adding this portion is making the plot static
max_heights = [0.5, .6, 1.0, 1.6, 0.1, 1.0]
rects = plt.bar(position, heights, align = 'center', color=['red', 'orange', 'blue', 'pink', 'green','purple'])
plt.xticks(position, ('Anger', 'Sadness', 'Disgust', 'Fear', 'Happy', 'Surprise'))
plt.xlabel('Emotion', color = '#072b57')
plt.ylabel('Probabilities', color = '#072b57')
plt.title('Emotion - Ally', color = '#072b57')
plt.ylim((0,1))
plt.xlim((0,6))
plt.grid(True)
rs = [r for r in rects]
def init():
return rs
def animate(i):
global rs, heights
if all(map(lambda x: x==1, heights)):
heights = [0, 0, 0, 0, 0, 0]
else:
heights = [min(h+s,mh) for h,s,mh in zip(heights,speeds,max_heights)]
# Bar plot should animate up to these values
# Adding this portion is making the plot static
#heights = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6]
print heights
for h,r in zip(heights,rs):
r.set_height(h)
return rs
anim = animation.FuncAnimation(fig, animate, init_func=init,frames=200, interval=20, blit=True)
plt.show()

You needs a little bit more calculation, e.g.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
fig = plt.figure()
position = np.arange(6) + .5
plt.tick_params(axis = 'x', colors = '#072b57')
plt.tick_params(axis = 'y', colors = '#072b57')
speeds = [.01, .02, .03, .04, .01, .02]
heights = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6]
rects = plt.bar(position, np.zeros_like(heights), align = 'center',
color=['red', 'orange', 'blue', 'pink', 'green','purple'])
plt.xticks(position, ('Anger', 'Sadness', 'Disgust', 'Fear', 'Happy', 'Surprise'))
plt.xlabel('Emotion', color = '#072b57')
plt.ylabel('Probabilities', color = '#072b57')
plt.title('Emotion - Ally', color = '#072b57')
plt.ylim((0,1))
plt.xlim((0,6))
plt.grid(True)
frames = 200
min_speed = np.min(speeds)
def init():
return rects
def animate(i):
for h,r,s in zip(heights,rects, speeds):
new_height = i / (frames-1) * h * s / min_speed
new_height= min(new_height, h)
r.set_height(new_height)
return rects
anim = animation.FuncAnimation(fig, animate, init_func=init,frames=frames, interval=20, blit=True, repeat=False)
plt.show()

Related

Matplotlib: correct placement of subplot annotations?

I want to create subplots with Matplotlib by looping over my data. However, I don't get the annotations into the correct position, apparently not even into the correct subplot. Also, the common x- and y-axis labels don't work.
My real data is more complex but here is an example that reproduces the error:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
import seaborn as sns
# create data
distributions = []
first_values = []
second_values = []
for i in range(4):
distributions.append(np.random.normal(0, 0.5, 100))
first_values.append(np.random.uniform(0.7, 1))
second_values.append(np.random.uniform(0.7, 1))
# create subplot
fig, axes = plt.subplots(2, 2, figsize = (15, 10))
legend_elements = [Line2D([0], [0], color = '#76A29F', lw = 2, label = 'distribution'),
Line2D([0], [0], color = '#FEB302', lw = 2, label = '1st value', linestyle = '--'),
Line2D([0], [0], color = '#FF5D3E', lw = 2, label = '2nd value')]
# loop over data and create subplots
for data in range(4):
if data == 0:
position = axes[0, 0]
if data == 1:
position = axes[0, 1]
if data == 2:
position = axes[1, 0]
if data == 3:
position = axes[1, 1]
dist = distributions[data]
first = first_values[data]
second = second_values[data]
sns.histplot(dist, alpha = 0.5, kde = True, stat = 'density', bins = 20, color = '#76A29F', ax = position)
sns.rugplot(dist, alpha = 0.5, color = '#76A29F', ax = position)
position.annotate(f'{np.mean(dist):.2f}', (np.mean(dist), 0.825), xycoords = ('data', 'figure fraction'), color = '#76A29F')
position.axvline(first, 0, 0.75, linestyle = '--', alpha = 0.75, color = '#FEB302')
position.axvline(second, 0, 0.75, linestyle = '-', alpha = 0.75, color = '#FF5D3E')
position.annotate(f'{first:.2f}', (first, 0.8), xycoords = ('data', 'figure fraction'), color = '#FEB302')
position.annotate(f'{second:.2f}', (second, 0.85), xycoords = ('data', 'figure fraction'), color = '#FF5D3E')
position.set_xticks(np.arange(round(min(dist), 1) - 0.1, round(max(max(dist), max([first]), max([second])), 1) + 0.1, 0.1))
plt.xlabel("x-axis name")
plt.ylabel("y-axis name")
plt.legend(handles = legend_elements, bbox_to_anchor = (1.5, 0.5))
plt.show()
The resulting plot looks like this:
What I want is to have
the annotations in the correct subplot next to the vertical lines / the mean of the distribution
shared x- and y-labels for all subplot or at least for each row / column
Any help is highly appreciated!
If you use the function to make the subplot a single array (axes.flatten()) and modify it to draw the graph sequentially, you can draw the graph. The colors of the annotations have been partially changed for testing purposes.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
import seaborn as sns
np.random.seed(202000104)
# create data
distributions = []
first_values = []
second_values = []
for i in range(4):
distributions.append(np.random.normal(0, 0.5, 100))
first_values.append(np.random.uniform(0.7, 1))
second_values.append(np.random.uniform(0.7, 1))
fig, axes = plt.subplots(2, 2, figsize=(15, 10))
legend_elements = [Line2D([0], [0], color = '#76A29F', lw = 2, label = 'distribution'),
Line2D([0], [0], color = '#FEB302', lw = 2, label = '1st value', linestyle = '--'),
Line2D([0], [0], color = '#FF5D3E', lw = 2, label = '2nd value')]
for i,ax in enumerate(axes.flatten()):
sns.histplot(distributions[i], alpha=0.5, kde=True, stat='density', bins=20, color='#76A29F', ax=ax)
sns.rugplot(distributions[i], alpha=0.5, color='#76A29F', ax=ax)
ax.annotate(f'{np.mean(distributions[i]):.2f}', (np.mean(distributions[i]), 0.825), xycoords='data', color='red')
ax.axvline(first_values[i], 0, 0.75, linestyle = '--', alpha = 0.75, color = '#FEB302')
ax.axvline(second_values[i], 0, 0.75, linestyle = '-', alpha = 0.75, color = '#FF5D3E')
ax.annotate(f'{first_values[i]:.2f}', (first_values[i], 0.8), xycoords='data', color='#FEB302')
ax.annotate(f'{second_values[i]:.2f}', (second_values[i], 0.85), xycoords='data', color = '#FF5D3E')
ax.set_xticks(np.arange(round(min(distributions[i]), 1) - 0.1, round(max(max(distributions[i]), max([first_values[i]]), max([second_values[i]])), 1) + 0.1, 0.1))
plt.xlabel("x-axis name")
plt.ylabel("y-axis name")
plt.legend(handles = legend_elements, bbox_to_anchor = (1.35, 0.5))
plt.show()

How to allow matplotlib bar chart to go beyond the border, so I have to pan to see the rest of the graph?

can someone help me with this graph plot, please? I have a categorical plot with 180 categories. I want to plot them in the same bar plot. To clearly visualize the graph I want to allow the x-axis to exceed the figure size, so I will view a part of the graph and I will have to pan to view the rest. Thanks.
there is my code:
#Data preparation
data = pd.DataFrame.spatial.from_featureclass("covid_cases")
df_temp = data.groupby("Country_Re")[["Confirmed", "Deaths", "Recovred"]].max().reset_index()
df_temp.drop(df_temp[(df_temp.Confirmed == 0) & (df_temp.Recovred == 0) & (df_temp.Deaths == 0)].index, inplace=True)
#Graph plot
ind = np.arange(df_temp.Country_Re.count())
f, ax = plt.subplots(figsize=(25,10))
p1 = ax.bar(ind - 0.25, df_temp.Confirmed, 0.25, color=(0.95, 0.62, 0.07, 1))
p2 = ax.bar(ind, df_temp.Recovred, 0.25, color=(0.12, 0.52, 0.29, 1))
p3 = ax.bar(ind + 0.25, df_temp.Deaths, 0.25, color=(1, 0, 0, 1))
ax1.set_title('Confirmed, Recovred and Deaths numbers by Countries.')
plt.xticks(ind, df_temp.Country_Re, rotation=90)
ax.set_yscale('symlog')
ax.legend((p1[0], p2[0], p3[0]), ('Confirmed', 'Recovred', 'Deaths'))
ax.margins(x=0.001)
plt.xticks(np.arange(0, ind.max()+1, 1.0))
plt.tick_params(axis="x", width=10)
plt.tight_layout()
plt.show()
The result:
You could consider using matplotlib slider widgets. The official demo can be found at https://matplotlib.org/3.3.0/gallery/widgets/slider_demo.html.
Here is a simplier version of it:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.25)
t = np.arange(0.0, 100.0, 0.1)
s = np.sin(2*np.pi*t)
plt.plot(t,s)
# xmin, xmax, ymin, ymax = axis([xmin, xmax, ymin, ymax])
# Define what you see when initially plotted
plt.axis([0, 10, -1, 1])
# plt.axes(rect, projection=None, polar=False, **kwargs)
# rect is a 4-tuple of floats = [left, bottom, width, height]
# A new axes is added with dimensions rect in normalized (0, 1) units using add_axes on the current figure.
# (left, bottom) specify lower left corner coordinates of the new axes in normalized (0, 1) units
axcolor = 'lightgoldenrodyellow'
axpos = plt.axes([0.2, 0.1, 0.65, 0.03], facecolor=axcolor)
# Slider(ax, label, valmin, valmax)
spos = Slider(axpos, 'Pos', 0.1, 90.0)
def update(val):
pos = spos.val
ax.axis([pos,pos+10,-1,1])
fig.canvas.draw_idle()
spos.on_changed(update)
plt.show()
Here is a bar plot example based on your code:
import math
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.25)
df = pd.DataFrame({'[0, 1)':np.random.rand(100),
'[1, 2)':np.random.rand(100)+1,
'[2, 3)':np.random.rand(100)+2})
n = math.ceil(df['[0, 1)'].count())
ind = np.arange(df['[0, 1)'].count())[:n]
p1 = ax.bar(ind - 0.25, df['[0, 1)'], 0.25, color=(0.95, 0.62, 0.07, 1))
p2 = ax.bar(ind, df['[1, 2)'], 0.25, color=(0.12, 0.52, 0.29, 1))
p3 = ax.bar(ind + 0.25, df['[2, 3)'], 0.25, color=(1, 0, 0, 1))
# xmin, xmax, ymin, ymax = axis([xmin, xmax, ymin, ymax])
# Define what you see when initially plotted
plt.axis([-1, 10, 0, 3])
# plt.axes(rect, projection=None, polar=False, **kwargs)
# rect is a 4-tuple of floats = [left, bottom, width, height]
# A new axes is added with dimensions rect in normalized (0, 1) units using add_axes on the current figure.
# (left, bottom) specify the new axes lower left corner coordinates in normalized (0, 1) units
axcolor = 'lightgoldenrodyellow'
axpos = plt.axes([0.2, 0.1, 0.65, 0.03], facecolor=axcolor)
# Slider(ax, label, valmin, valmax)
spos = Slider(axpos, 'Pos', -0.2, 90.0)
def update(val):
pos = spos.val
ax.axis([pos,pos+10,0,3])
fig.canvas.draw_idle()
spos.on_changed(update)
plt.show()
I didn't found an adequate solution, so I plotted my graph into two subplots.
There is the code :
#Data preparation
data = pd.DataFrame.spatial.from_featureclass("covid_cases")
df_temp = data.groupby("Country_Re")[["Confirmed", "Deaths", "Recovred"]].max().reset_index()
df_temp.drop(df_temp[(df_temp.Confirmed == 0) & (df_temp.Recovred == 0) & (df_temp.Deaths == 0)].index, inplace=True)
#Graph Plot
n = math.ceil(df_temp.Country_Re.count() / 2)
ind = np.arange(df_temp.Country_Re.count())[:n]
f, (ax1, ax2) = plt.subplots(nrows=2, figsize=(20, 10), dpi=70)
p1 = ax1.bar(ind - 0.25, df_temp.Confirmed.head(n), 0.25, color=(0.95, 0.62, 0.07, 1))
p2 = ax1.bar(ind, df_temp.Recovred.head(n), 0.25, color=(0.12, 0.52, 0.29, 1))
p3 = ax1.bar(ind + 0.25, df_temp.Deaths.head(n), 0.25, color=(1, 0, 0, 1))
p4 = ax2.bar(ind - 0.25, df_temp.Confirmed.tail(n), 0.25, color=(0.95, 0.62, 0.07, 1))
p5 = ax2.bar(ind, df_temp.Recovred.tail(n), 0.25, color=(0.12, 0.52, 0.29, 1))
p6 = ax2.bar(ind + 0.25, df_temp.Deaths.tail(n), 0.25, color=(1, 0, 0, 1))
plt.sca(ax1)
ax1.set_title('Confirmed, Recovred and Deaths numbers by Country_Re.')
plt.xticks(ind, df_temp.Country_Re.head(n), rotation=90)
ax1.set_yscale('symlog')
ax1.legend((p1[0], p2[0], p3[0]), ('Confirmed', 'Recovred', 'Deaths'))
ax1.margins(x=0.001)
plt.tick_params(axis="x", width=10)
plt.tight_layout()
plt.sca(ax2)
ax2.set_title('Confirmed, Recovred and Deaths numbers by Country_Re.')
plt.xticks(ind, df_temp.Country_Re.tail(n), rotation=90)
ax2.set_yscale('symlog')
ax2.legend((p4[0], p5[0], p6[0]), ('Confirmed', 'Recovred', 'Deaths'))
ax2.margins(x=0.001)
plt.tick_params(axis="x", width=10)
plt.tight_layout()
plt.show()
There is the result:

How to build a horizontal stacked bar graph from positive to negative percentages with values displayed using Python

I have a data frame but I am trying to build a graph and I am having a hard time. Can you guys please help me with this? I tried to google but I am not able to find a valid resource.
Friend, your graph caught my eye, and I decided to create it. I learned a lot in the process, and I hope it may be helpful to you:
Input data:
Completely Dissatisfied Somewhat Dissatisfied Dissatisfied Neither Satisfied nor Dissatisfied Somewhat Satisfied Satisfied Completely Satisfied
Q1 10 10 10 10 10 20 30
Q2 0 0 20 20 30 20 10
Q3 10 20 0 30 20 20 20
Q4 0 0 10 10 30 20 10
Plotting code:
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import numpy as np
import pandas as pd
# Read in data
df = pd.read_csv('plot_data.csv', index_col=0)
df = df/100 # Convert to percentages
def custom_plot(df, example=False):
def plot_rect(bottom, left, width, height, color = 'C0'):
ax.add_patch(patches.Rectangle(
(left, bottom), width, height, linewidth=1, edgecolor=color, facecolor=color))
# Create figure and axes
fig, ax = plt.subplots(1)
# Define axis ticks ticks
plt.xticks(np.arange(-1,1.25,0.25), np.arange(-100,125,25))
plt.yticks(np.arange(0,1.2,0.2), np.arange(0,1.2,0.2))
# Define axis limits
plt.ylim(0.05,0.95)
plt.xlim(-1.125, 1.125)
# Move gridlines to the back of the plot
plt.gca().set_axisbelow(True)
# Change color of plot edges
ax.spines['left'].set_color('lightgray')
ax.spines['right'].set_color('lightgray')
ax.spines['top'].set_color('lightgray')
# Hide y axis ticks
plt.gca().tick_params(axis='y', colors='w')
# Turn on gridlines and set color
plt.grid(b=True, axis='both', color='lightgray', alpha=0.5, linewidth=1.5)
# Add lines
plt.axvline(x=0, c='lightgray')
plt.axhline(y=0.5, c='black')
# Add x label
plt.xlabel('Percent', fontsize=14)
# Define color scheme from negative to positive
colors = ['firebrick', 'sandybrown', 'navajowhite',
'khaki', 'lightcyan', 'skyblue', 'steelblue']
# Process data to plot
try:
array = [df.iloc[0,:].values,
df.iloc[1,:].values,
df.iloc[2,:].values,
df.iloc[3,:].values]
except:
print('Plotting example data')
example = True
if example == True:
# Example data
array = [np.array([0.05, 0.1, 0.2, 0.2, 0.3, 0.1, 0.05]),
np.array([0, 0.1, 0.1, 0.3, 0.2, 0.2, 0.1]),
np.array([0.1, 0.2, 0.2, 0.3, 0.1, 0.05, 0.05]),
np.array([0.2, 0.1, 0.2, 0.2, 0.1, 0.1, 0.1])]
# Example data column names
df = pd.DataFrame(columns=['Completely Dissatisfied',
'Somewhat Dissatisfied',
'Dissatisfied',
'Neither Satisfied nor Dissatisfied',
'Somewhat Satisfied',
'Satisfied',
'Completely Satisfied'])
# Compute average statistics
hi = [sum(x[4:]) for x in array]
med = [x[3] for x in array]
lo = [sum(x[0:3]) for x in array]
# Define function to process input data into rectangle left corner indices
def process_data(array):
left = np.zeros_like(array)
mid = array[3]/2
left[0] = -np.sum(array[0:3]) - mid
left[1] = -np.sum(array[1:3]) - mid
left[2] = -np.sum(array[2:3]) - mid
left[3] = -mid
left[4] = mid
left[5] = np.sum(array[4:5]) + mid
left[6] = np.sum(array[4:6]) + mid
width = array
return left, width
left = {}
width = {}
for i in range(4):
left[i], width[i] = process_data(array[i])
# Plot boxes
height = 0.13
bottom = 0.135
for i in range(len(array)):
for j in range(len(array[i])):
plot_rect(bottom=bottom+i*0.2, left=left[i][j], width=width[i][j], height=height, color = colors[j])
# Plot category labels
plt.text(-1.1,0.9,'Unfavorable', style='italic',
horizontalalignment='left', verticalalignment='center')
plt.text(0,0.9,'Neutral', style='italic',
horizontalalignment='center', verticalalignment='center')
plt.text(1.1,0.9,'Favorable', style='italic',
horizontalalignment='right', verticalalignment='center')
# Plot percentages
for i in range(len(med)):
plt.text(-1,0.2*(i+1),'{0:.0%}'.format(lo[i]),
horizontalalignment='left', verticalalignment='center')
plt.text(0,0.2*(i+1),'{0:.0%}'.format(med[i]),
horizontalalignment='center', verticalalignment='center')
plt.text(1,0.2*(i+1),'{0:.0%}'.format(hi[i]),
horizontalalignment='right', verticalalignment='center')
# Create legend
fig, ax = plt.subplots(1, figsize=(6,2))
plt.axis('off')
plt.gca().set_aspect('equal', adjustable='box')
# Plot colored circles
legend_left = [-0.9, -0.6, -0.3, 0, 0.30, 0.6, 0.9]
for i in range(len(colors)):
plot_rect(bottom=0, left=legend_left[i], width=0.2, height=0.2, color = colors[i])
# Plot labels 1-6
for i in range(0,6,2):
plt.text(-0.8+0.3*i, 0.25, df.columns[i].replace(' ', '\n'),
horizontalalignment='center', verticalalignment='bottom')
plt.text(-0.5+0.3*i, -0.05, df.columns[i+1].replace(' ', '\n'),
horizontalalignment='center', verticalalignment='top')
# Plot last label
plt.text(1, 0.25, df.columns[6].replace(' ', '\n'),
horizontalalignment='center', verticalalignment='bottom')
# Plot label title
plt.text(-1, 0.1, 'Scale', fontsize=14,
horizontalalignment='right', verticalalignment='center')
plt.gca().autoscale(enable=True, axis='both', tight=None)
#custom_plot('example')
custom_plot(df)

How to efficiently animate multiple patches from a list of lists

I am trying to animate multiple patches as efficiently as possible when reading data from a list?
The code below displays an animation of the scatter plot but not the patches. Each point in scatter plot contains various sizes of circles. This example would require 6 different circles to be animated at 2 subjects each time point. But what if there were 20 subjects that each had 3 circles around them.
What is the most efficient way to animate all 60 circles for each frame?
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib as mpl
x_data = np.random.randint(80, size=(400, 4))
y_data = np.random.randint(80, size=(400, 4))
fig, ax = plt.subplots(figsize = (8,6))
ax.set_xlim(0,80)
ax.set_ylim(0,80)
scatter = ax.scatter(x_data[0], y_data[0], zorder = 5) #Scatter plot
Player_1 = x_data[0][0], y_data[0][0]
Player_2 = x_data[0][1], y_data[0][1]
Player_1_IR = mpl.patches.Circle(Player_1, radius = 2, color = 'black', lw = 1, alpha = 0.8, zorder = 4)
Player_1_MR = mpl.patches.Circle(Player_1, radius = 4, color = 'gray', lw = 1, alpha = 0.8, zorder = 3)
Player_1_OR = mpl.patches.Circle(Player_1, radius = 6, color = 'lightgrey', lw = 1, alpha = 0.8, zorder = 2)
Player_2_IR = mpl.patches.Circle(Player_2, radius = 2, color = 'black', lw = 1, alpha = 0.8, zorder = 4)
Player_2_MR = mpl.patches.Circle(Player_2, radius = 4, color = 'gray', lw = 1, alpha = 0.8, zorder = 3)
Player_2_OR = mpl.patches.Circle(Player_2, radius = 6, color = 'lightgrey', lw = 1, alpha = 0.8, zorder = 2)
ax.add_patch(Player_1_IR)
ax.add_patch(Player_1_MR)
ax.add_patch(Player_1_OR)
ax.add_patch(Player_2_IR)
ax.add_patch(Player_2_MR)
ax.add_patch(Player_2_OR)
def animate(i) :
scatter.set_offsets(np.c_[x_data[i,:], y_data[i,:]])
ani = animation.FuncAnimation(fig, animate, frames=len(x_data),
interval = 700, blit = False)
plt.show()
You can store all patches that you want to update in a list through which you then iterate through every iteration step. Note that the size of the Circle patches is in data units/coordinates while the scatter plot points are in points (one point = 1/72 inch), which means that the relative size between scatter points and circles depends on the figure size and axes limits and will change when you re-scale the figure.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib as mpl
x_data = np.random.randint(80, size=(400, 20))
y_data = np.random.randint(80, size=(400, 20))
fig, ax = plt.subplots(figsize = (8,6))
ax.set_xlim(0,80)
ax.set_ylim(0,80)
scatter = ax.scatter(x_data[0], y_data[0], zorder = 5) #Scatter plot
##creating list of patches
players = []
for n in range(10):
##as there are always 3 circles, append all three patches as a list at once
players.append([
mpl.patches.Circle((x_data[0,n],y_data[0,n]), radius = 2, color = 'black', lw = 1, alpha = 0.8, zorder = 4),
mpl.patches.Circle((x_data[0,n],y_data[0,n]), radius = 4, color = 'gray', lw = 1, alpha = 0.8, zorder = 3),
mpl.patches.Circle((x_data[0,n],y_data[0,n]), radius = 6, color = 'lightgrey', lw = 1, alpha = 0.8, zorder = 2)
])
##adding patches to axes
for player in players:
for circle in player:
ax.add_patch(circle)
def animate(i):
scatter.set_offsets(np.c_[x_data[i,:], y_data[i,:]])
##updating players:
for n,player in enumerate(players):
for circle in player:
circle.center = (x_data[i,n],y_data[i,n])
ani = animation.FuncAnimation(fig, animate, frames=len(x_data),
interval = 700, blit = False)
plt.show()
Old Answer (slightly different visual effect, but could be tuned to look the same):
If you really just want circles around your scatter points, you can actually forget about the Circle patches and just overlay several scatter plots with different marker sizes.
In the example below I only mark part of the scatter points with circles by slicing the array of random numbers. Also remember that in scatter plots the marker size is given as points square, so if you want to increase the circle radius from, say, 5 to 6, the given marker size should change from 25 to 36.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib as mpl
x_data = np.random.randint(80, size=(400, 20))
y_data = np.random.randint(80, size=(400, 20))
fig, ax = plt.subplots(figsize = (8,6))
ax.set_xlim(0,80)
ax.set_ylim(0,80)
scatter = ax.scatter(x_data[0], y_data[0], zorder = 5) #Scatter plot
scatter_IR = ax.scatter(
x_data[0,:10], y_data[0,:10], zorder = 4,
facecolor='black', edgecolor = 'black',
alpha = 0.8, s = 100
)
scatter_MR = ax.scatter(
x_data[0,:10], y_data[0,:10], zorder = 3,
facecolor='grey', edgecolor = 'grey',
alpha = 0.8, s = 225
)
scatter_OR = ax.scatter(
x_data[0,:10], y_data[0,:10], zorder = 2,
facecolor='lightgrey', edgecolor = 'lightgrey',
alpha = 0.8, s = 400
)
def animate(i) :
scatter.set_offsets(np.c_[x_data[i,:], y_data[i,:]])
scatter_IR.set_offsets(np.c_[x_data[i,:10], y_data[i,:10]])
scatter_MR.set_offsets(np.c_[x_data[i,:10], y_data[i,:10]])
scatter_OR.set_offsets(np.c_[x_data[i,:10], y_data[i,:10]])
ani = animation.FuncAnimation(fig, animate, frames=len(x_data),
interval = 700, blit = False)
plt.show()

Polar chart with limit and anomalous points

Consider the following data frame,
d = {'Score': [0.25, 0.52, 0.26, 0.22, 0.31, 2.45, 3.68, 41.3, 87, 91],
'Thr1': 16.5,
'Thr2': 45.5,
'Anomaly':[0, 0, 0, 0, 0, 0, 0, 1, 1, 1]}
df = pd.DataFrame(data = d)
What I m trying to do is to plot a polar chart, with a dotted line for threshold or multiple dotted lines for multiple thresholds and different color for the anomalies. What I ve got so far is,
r = df['Score']
theta = df.index.values
fig = plt.figure()
ax = fig.add_subplot(111, projection = 'polar')
c = ax.scatter(theta, r)
I cannot get the threshold though and change the color of the anomalous points. Any ideas?
You need to draw a dashed line at the threshold level, to indicate where the threshold is. (a line will appear as a circle on a polar plot).
Then you need to segregate the values to plot on the scatter plot, based whether or not they are below, between, or above the thresholds, and color the points accordingly.
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
dataset = {'Score': [0.25, 0.52, 0.26, 0.22, 0.31, 2.45, 3.68, 41.3, 87, 91],
'Thr1': 16.5,
'Thr2': 45.5,
'Anomaly':[0, 0, 0, 0, 0, 0, 0, 1, 1, 1]}
df = pd.DataFrame(data=dataset)
scores = df['Score']
theta, thr_1, thr_2 = df.index.values, dataset['Thr1'], dataset['Thr2']
fig = plt.figure()
ax = fig.add_subplot(111, projection='polar')
# assigns a color to each point based on their relative value to the thresholds
colors = ['b' if val < thr_1 else 'y' if val < thr_2 else 'r' for val in scores]
point_cloud = ax.scatter(theta, scores, color=colors, marker='o')
# Drawing the threshold dash lines (with alpha value 1/2)
theta_xs, thr_y1, thr_y2 = np.linspace(0, 2*np.pi, 20), [thr_1] * 20, [thr_2] * 20
thr_line_1 = ax.plot(theta_xs, thr_y1, color='blue', linestyle='--', alpha=0.5)
thr_line_2 = ax.plot(theta_xs, thr_y2, color='green', linestyle='--', alpha=0.5)
plt.show()
Well, i'm not exactly sure that it is what you want, because i never used Anomaly part of your dataset, and just take color info from Score array
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.colors as c
d = {'Score': [0.25, 0.52, 0.26, 0.22, 0.31, 2.45, 3.68, 41.3, 87, 91],
'Thr1': 16.5,
'Thr2': 45.5,
'Anomaly': [0, 0, 0, 0, 0, 0, 0, 1, 1, 1]}
df = pd.DataFrame(data = d)
r = df['Score']
theta = df.index.values
fig = plt.figure()
ax = fig.add_subplot(111, projection = 'polar')
#Add thresholds
ax.plot(np.linspace(0, 2*np.pi, 100), np.ones(100)*d['Thr1'], c='g', ls='--')
ax.plot(np.linspace(0, 2*np.pi, 100), np.ones(100)*d['Thr2'], c='r', ls='--')
#Add colors
colors = ['g' if v < d['Thr1'] else 'y' if v < d['Thr2'] else "r" for v in r]
sc = ax.scatter(theta, r, c=colors)
plt.show()

Categories

Resources