Matplotlib radar chart - python

so I was experimenting with this matplotlib example. In the data part I tried to make the graphic from a tuple like this:
data = data[0:8]
f1_CO = [0.88, 0.02, 0.02, 0.02, 0.00, 0.05, 0.00, 0.05, 0.00]
f1_O3 = [0.89, 0.01, 0.07, 0.00, 0.00, 0.05, 0.00, 0.00, 0.03]
f1_both = [0.86, 0.01, 0.08, 0.00, 0.00, 0.04, 0.00, 0.00, 0.01]
But it go wrong because it says:
ValueError: x and y must have same first dimension
So, what do I have to change to make it possible to make a plot from a tuple?
The complete code is :
from matplotlib.projections.polar import PolarAxes
from matplotlib.projections import register_projection
from pylab import *
import wave
import struct
def radar_factory(num_vars, frame='polygon'):
"""Create a radar chart with `num_vars` axes.
"""
# calculate evenly-spaced axis angles
theta = 2*pi * linspace(0, 1-1/int(num_vars), num_vars)
#print theta
#print
# rotate theta such that the first axis is at the top
theta += pi/2
def draw_poly_frame(self, x0, y0, r):
# TODO: should use transforms to convert (x, y) to (r, theta)
verts = [(r*cos(t) + x0, r*sin(t) + y0) for t in theta]
return Polygon(verts, closed=True)
def draw_circle_frame(self, x0, y0, r):
return Circle((x0, y0), r)
frame_dict = {'polygon': draw_poly_frame, 'circle': draw_circle_frame}
if frame not in frame_dict:
raise ValueError, 'unknown value for `frame`: %s' % frame
class RadarAxes(PolarAxes):
"""Class for creating a radar chart (a.k.a. a spider or star chart)
http://en.wikipedia.org/wiki/Radar_chart
"""
name = 'radar'
# use 1 line segment to connect specified points
RESOLUTION = 1
# define draw_frame method
draw_frame = frame_dict[frame]
def fill(self, *args, **kwargs):
"""Override fill so that line is closed by default"""
closed = kwargs.pop('closed', True)
return super(RadarAxes, self).fill(closed=closed, *args,**kwargs)
def plot(self, *args, **kwargs):
"""Override plot so that line is closed by default"""
lines = super(RadarAxes, self).plot(*args, **kwargs)
for line in lines:
self._close_line(line)
def _close_line(self, line):
x, y = line.get_data()
# FIXME: markers at x[0], y[0] get doubled-up
if x[0] != x[-1]:
x = concatenate((x, [x[0]]))
y = concatenate((y, [y[0]]))
line.set_data(x, y)
def set_varlabels(self, labels, rvals, rlabels):
self.set_thetagrids(theta * 180/pi, labels)
self.set_rgrids(rvals, labels=rlabels, size='small')
def get_axes_patch(self):
x0, y0 = (0.5, 0.5)
r = 0.5
return self.draw_frame(x0, y0, r)
register_projection(RadarAxes)
return theta
if __name__ == '__main__':
w = wave.open('C:/Python27/demo1.wav','r')
nf = w.getnframes()
sw = w.getsampwidth()
assert(sw==2)
rf = w.readframes(nf)
w.close()
data = struct.unpack("%sh" %nf,rf)
for i in range(9):
print i,data[i]
N = 9
theta = radar_factory(N)
data = data[0:8]
f1_CO = [0.88, 0.02, 0.02, 0.02, 0.00, 0.05, 0.00, 0.05, 0.00]
f1_O3 = [0.89, 0.01, 0.07, 0.00, 0.00, 0.05, 0.00, 0.00, 0.03]
f1_both = [0.86, 0.01, 0.08, 0.00, 0.00, 0.04, 0.00, 0.00, 0.01]
f2_base = [0.07, 0.95, 0.04, 0.05, 0.00, 0.02, 0.01, 0.00, 0.00]
f2_CO = [0.08, 0.94, 0.04, 0.02, 0.00, 0.01, 0.12, 0.04, 0.00]
f2_O3 = [0.07, 0.95, 0.05, 0.04, 0.00, 0.02, 0.12, 0.00, 0.00]
f2_both = [0.09, 0.95, 0.02, 0.03, 0.00, 0.01, 0.13, 0.06, 0.00]
f3_base = [0.01, 0.02, 0.85, 0.19, 0.05, 0.10, 0.00, 0.00, 0.00]
f3_CO = [0.01, 0.01, 0.79, 0.10, 0.00, 0.05, 0.00, 0.31, 0.00]
f3_O3 = [0.01, 0.02, 0.86, 0.27, 0.16, 0.19, 0.00, 0.00, 0.00]
f3_both = [0.01, 0.02, 0.71, 0.24, 0.13, 0.16, 0.00, 0.50, 0.00]
f4_base = [0.01, 0.01, 0.02, 0.71, 0.74, 0.70, 0.00, 0.00, 0.00]
f4_CO = [0.00, 0.02, 0.03, 0.38, 0.31, 0.31, 0.00, 0.59, 0.00]
f4_O3 = [0.01, 0.03, 0.00, 0.32, 0.29, 0.27, 0.00, 0.00, 0.95]
f4_both = [0.01, 0.03, 0.00, 0.28, 0.24, 0.23, 0.00, 0.44, 0.88]
f5_base = [0.02, 0.01, 0.07, 0.01, 0.21, 0.12, 0.98, 0.00, 0.00]
f5_CO = [0.02, 0.02, 0.11, 0.47, 0.69, 0.58, 0.88, 0.00, 0.00]
f5_O3 = [0.02, 0.00, 0.03, 0.37, 0.56, 0.47, 0.87, 0.00, 0.00]
f5_both = [0.02, 0.00, 0.18, 0.45, 0.64, 0.55, 0.86, 0.00, 0.16]
fig = figure(figsize=(9,9))
fig.subplots_adjust(wspace=0.25, hspace=0.20)
axlist = []
axisNum = 0
bases = [data, f2_base, f3_base, f5_base, f4_base]
COs = [f1_CO, f2_CO, f3_CO, f4_CO, f5_CO]
O3s = [f1_O3, f2_O3, f3_O3, f4_O3, f5_O3]
boths = [f1_both, f2_both, f3_both, f4_both, f5_both]
everything = [bases, COs, O3s, boths]
titles = ['Muestreo 1', 'Muestreo 2', 'Muestreo 3', 'Muestreo 4']
colors = ['b', 'r', 'g', 'm', 'y']
for row in range(2):
for col in range(2):
axisNum += 1
if axisNum == 2:
#Unfortunately, it looks like the loc keyword to legend() is
#relative to a specific subplot, rather than the figure itself.
#So, the positioning seen looks good, but if you resize the
#figure to be larger the legend becomes obviously bound to a
#specific subplot. This is in contrast to how the position works
#in something like figtext(). Had trouble using figlegend(), but
#need to try some more...
legend(('Factor 1', 'Factor 2', 'Factor 3', 'Factor 4',
'Factor 5'), loc=(0.95, 0.895), borderpad=0.01,
shadow=False, prop=matplotlib.font_manager
.FontProperties(size='smaller'), markerscale=0.4)
data = everything[axisNum-1]
ax = fig.add_subplot(2, 2, axisNum, projection='radar')
ax.set_title(titles[axisNum-1], weight='bold', size='medium',
horizontalalignment='center',
verticalalignment='center',
position=(0.5, 1.1))
p1 = ax.plot(theta, data[0], color=colors[0])
p2 = ax.plot(theta, data[1], color=colors[1])
p3 = ax.plot(theta, data[2], color=colors[2])
p4 = ax.plot(theta, data[3], color=colors[3])
p5 = ax.plot(theta, data[4], color=colors[4])
ax.fill(theta, data[0], facecolor=colors[0])
ax.fill(theta, data[1], facecolor=colors[1])
ax.fill(theta, data[2], facecolor=colors[2])
ax.fill(theta, data[3], facecolor=colors[3])
ax.fill(theta, data[4], facecolor=colors[4])
#axlist.extend(ax) #This does not work because ax is a
#RadarAxesSubplot object, which is not iterable
axlist.append(ax) #append() works because it simply tacks on to
#the list, as opposed to merging items from two
#lists
for patch in ax.patches:
patch.set_alpha(0.25)
figtext(0.5, 0.965, '5-Factor Solution Profiles Across Four Scenarios', ha='center', color='black', weight='bold', size='large')
#Crudely plot the grid lines I want to see: normalized concentrations of
#chemicals range from 0 to 1...
radiiGrid = [0.2, 0.4, 0.6, 0.8]
theta_rgrid = radar_factory(100)
for ax in axlist:
for r in radiiGrid:
radii = repeat(r, 100)
ax.plot(theta_rgrid, radii, color='lightgrey')
# FIXME: legend doesn't work when fill is called
spokeLabels = ['Sulfate', 'Nitrate', 'EC', 'OC1', 'OC2', 'OC3', 'OP',
'CO',
'O3']
radiiLabels = [str(rg) for rg in radiiGrid]
for ax in axlist:
ax.set_varlabels(spokeLabels, radiiGrid, radiiLabels)
show()

The problem doesn't come from the tuple type.
It seems that data[0] (length = 9) and theta (length = 8) haven't the same length (first dimension).
You have to put data = data[0:9]

Related

Number of columns does not match number of bins

I am trying to plot some 336 data points and am encountering an issue with my use of pythons plt.hist() function. I would like to use more than eight bins for my data, but when I do a lot of whitespace is introduced. For example, here is a plot with bins = 8
and with bins = 24
Does anyone know why this is and how I can best represent my data with more bins? Many thanks, ~S.
Sample code:
tumbles = np.array(df['Tumbles'])
fig, axs = plt.subplots(1, 1,
tight_layout = True)
N, bins, patches = axs.hist(tumbles, bins = 24, edgecolor= "black")
axs.grid(b = True, color ='grey',
linestyle ='-.', linewidth = 0.5,
alpha = 0.6)
plt.xlabel("Time (s)", size = 14)
plt.ylabel("Frequency", size = 14)
plt.title('Histogram ofTimes', size = 18)
plt.show()
I feel like your data is distributed in a way that the empty space between bars are simply bars with height 0 (a lack of samples). In such a case you just don't need more bins.
Please include your code
With this setup I get the same problem:
import matplotlib.pyplot as plt
plt.hist([1, 2, 2, 3, 4, 5, 5, 5, 5, 5, 6, 7, 9], bins=20)
plt.show()
It would be a bit more effort, but if you want a bit more control over the number of bins and the range of each bin, you might set up the bin parameter in your histogram definition as a list. This was alluded to above, but here is a snippet of code illustrating that.
import matplotlib.pyplot as plt
data = [0.02, 0.02, 0.02, 0.27, 0.27, 0.03, 0.03, 0.04, 0.044, 0.044, 0.05, 0.05, 0.06, 0.07, 0.08, 0.08, 0.08, 0,10, 0.10, 0.11, 0.12, 0.13, 0.13, 0.14, 0.15, 0.17, 0,18, 0.19, 0.20, 0.20, 0.22, 0.23, 0.23, 0.23, 0.23, 0.24, 0.26, 0.26, 0.28, 0.29, 0.30, 0.32]
fig, ax = plt.subplots()
N, bins, values = ax.hist(data, [0.000,0.015,0.030,0.045,0.060,0.075,0.090,0.105,0.120,0.135,0.150,0.165,0.180,0.195,0.210,0.225,0.240,0.255,0.270,0.285,0.300,0.315,0.330,0.345], linewidth=1)
plt.bar_label(values)
plt.xlabel("Time (s)", size = 14)
plt.ylabel("Frequency", size = 14)
plt.title('Histogram of Times', size = 18)
plt.show()
The data is just a small subset to establish some data points to produce a histogram. Following was the histogram created in this fashion.
You might give that a try adjusting for the range each bin should have.

Distance betweeen groups in a matplotlib bar plot [duplicate]

This question already has answers here:
How to create a grouped bar plot
(4 answers)
Closed 7 months ago.
Here is how I bar-plot from a group:
import numpy as np
import matplotlib.pyplot as plt
metrics = ['accuracy', 'precision', 'recall','f1_score', 'roc_auc_score']
x_values = np.arange(len(metrics))
width = 0.15
RF = [0.62, 0.59, 0.62, 0.57, 0.78]
SMOTE = [0.63, 0.62, 0.63, 0.60, 0.79]
AdaBoost = [0.27, 0.42, 0.27, 0.28, 0.58]
SMOTEBoost = [0.54, 0.60, 0.54, 0.57, 0.68]
decoc = [0.63, 0.61, 0.63, 0.58, 0.69]
plt.bar(x_values-0.2, RF, width=width, label='RF')
plt.bar(x_values, SMOTE, width=width, label='SMOTE')
plt.bar(x_values+0.2, AdaBoost, width=width, label='AdaBoost')
plt.bar(x_values+0.4, SMOTEBoost, width=width, label='SMOTEBoost')
plt.bar(x_values+0.6, decoc, width=width, label='DECOC')
plt.xticks(x_values, metrics)
plt.legend(loc='best')
plt.ylim(0.0, 1.2)
plt.title('Performance Evaluation')
plt.xlabel('Performance Metrics')
plt.show()
Figure:
But I need a space between each group ('accuracy', 'precision', 'recall','f1_score', 'roc_auc_score') to make it better. As it is, groups are mixed with almost no space separating.
Any reason to do multiple bar plots instead of one and pass the hue variable?
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
plt.rcParams["figure.figsize"] = (16,8)
RF = [0.62, 0.59, 0.62, 0.57, 0.78]
SMOTE = [0.63, 0.62, 0.63, 0.60, 0.79]
AdaBoost = [0.27, 0.42, 0.27, 0.28, 0.58]
SMOTEBoost = [0.54, 0.60, 0.54, 0.57, 0.68]
decoc = [0.63, 0.61, 0.63, 0.58, 0.69]
metrics = ['accuracy', 'precision', 'recall','f1_score', 'roc_auc_score']
df = pd.DataFrame({"metrics":metrics,"RF":RF, "SMOTE":SMOTE,"AdaBoost":AdaBoost,"SMOTEBoost":SMOTEBoost,"decoc":decoc})
df = pd.melt(df, id_vars="metrics")
sns.barplot(data=df, x="metrics", y="value", hue="variable")
plt.legend(loc='best')
plt.ylim(0.0, 1.2)
plt.title('Performance Evaluation')
plt.xlabel('Performance Metrics')
plt.show()
Increase the spacing between the x-values:
x_values = np.arange(0, len(metrics)*2, 2)

How to colormap errorbars (x and y) in a scatter plot with a dataset (nd.array)?

I'm trying to create a scatter plot with x and y errors that have different marker and errorbar colors in four sections (e.g. red for x=0 to x=2, blue for x=2 to c=5, etc.). I have used a colormap with bounds for the markers, but I haven't been able to do something similar for the errorbars. I've tried to set the markers, errorbars, and caps as the same color in the scatter colormap using this answer to a similar question, but I wasn't able to get it to work for my code (comes up with an error about lengths of data not matching or unable to convert to tuple). I think I haven't been able to correctly modify it for the colormap I use for the markers, or this isn't the best way to go about getting the right result.
This is an example with some made up data:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors
bounds = [0,1.5,3,4.5,5]
colors = ["r", "b", "g", "y"]
cmap = matplotlib.colors.ListedColormap(colors)
norm = matplotlib.colors.BoundaryNorm(bounds, len(colors))
x = np.array([0.0, 0.0, 1.0, 2.0, 2.0, 3.0, 4.0, 4.0, 5.0, 5.0])
y = np.array([0.0, 0.1, 0.8, 0.9, 0.7, 0.1, -0.8, -0.5, -1.0, -0.7])
x_err = np.array([0.05, 0.06, 0.04, 0.045, 0.04, 0.06, 0.05, 0.055, 0.02, 0.05])
y_err = np.array([0.04, 0.05, 0.03, 0.055, 0.145, 0.065, 0.045, 0.15, 0.015, 0.17])
plt.scatter(x, y, marker='D', c=x, cmap=cmap, norm=norm)
plt.errorbar(x, y, xerr=x_err, yerr=y_err, fmt='.', lw=2, capsize=3, alpha=0.7, zorder=0)
plt.show()
which gives
.
How can I get the errorbars to have the same colormap as the one used in the scatter plot?
This is certainly not the fastest method but it works: get the colors for each x-value using to_rgba and then plot the error bars pointwise (probably slow for large data arrays):
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors
import matplotlib.cm
bounds = [0,1.5,3,4.5,5]
colors = ["r", "b", "g", "y"]
cmap = matplotlib.colors.ListedColormap(colors)
norm = matplotlib.colors.BoundaryNorm(bounds, len(colors))
x = np.array([0.0, 0.0, 1.0, 2.0, 2.0, 3.0, 4.0, 4.0, 5.0, 5.0])
y = np.array([0.0, 0.1, 0.8, 0.9, 0.7, 0.1, -0.8, -0.5, -1.0, -0.7])
x_err = np.array([0.05, 0.06, 0.04, 0.045, 0.04, 0.06, 0.05, 0.055, 0.02, 0.05])
y_err = np.array([0.04, 0.05, 0.03, 0.055, 0.145, 0.065, 0.045, 0.15, 0.015, 0.17])
plt.scatter(x, y, marker='D', c=x, cmap=cmap, norm=norm)
colors = matplotlib.cm.ScalarMappable(norm,cmap).to_rgba(x)
for i,_ in enumerate(x):
plt.errorbar(x[i], y[i], xerr=x_err[i], yerr=y_err[i], fmt='.', lw=2, capsize=3, alpha=0.7, zorder=0, ecolor=colors[i])
plt.show()

In a radar chart in matplotlib, is there any way to stagger the positions of the yticks (or rticks) so that they do not appear in the same line?

I have the following code from the matplotlib demo:
import matplotlib.pyplot as plt
from matplotlib.path import Path
from matplotlib.spines import Spine
from matplotlib.projections.polar import PolarAxes
from matplotlib.projections import register_projection
import glob
import pygal
import pandas as pd
import numpy as np
def radar_factory(num_vars, frame='circle'):
"""Create a radar chart with `num_vars` axes.
This function creates a RadarAxes projection and registers it.
Parameters
----------
num_vars : int
Number of variables for radar chart.
frame : {'circle' | 'polygon'}
Shape of frame surrounding axes.
"""
# calculate evenly-spaced axis angles
theta = np.linspace(0, 2*np.pi, num_vars, endpoint=False)
# rotate theta such that the first axis is at the top
theta += np.pi/2
def draw_poly_patch(self):
verts = unit_poly_verts(theta)
return plt.Polygon(verts, closed=True, edgecolor='k')
def draw_circle_patch(self):
# unit circle centered on (0.5, 0.5)
return plt.Circle((0.5, 0.5), 0.5)
patch_dict = {'polygon': draw_poly_patch, 'circle': draw_circle_patch}
if frame not in patch_dict:
raise ValueError('unknown value for `frame`: %s' % frame)
class RadarAxes(PolarAxes):
name = 'radar'
# use 1 line segment to connect specified points
RESOLUTION = 1
# define draw_frame method
draw_patch = patch_dict[frame]
def fill(self, *args, **kwargs):
"""Override fill so that line is closed by default"""
closed = kwargs.pop('closed', True)
return super(RadarAxes, self).fill(closed=closed, *args, **kwargs)
def plot(self, *args, **kwargs):
"""Override plot so that line is closed by default"""
lines = super(RadarAxes, self).plot(*args, **kwargs)
for line in lines:
self._close_line(line)
def _close_line(self, line):
x, y = line.get_data()
# FIXME: markers at x[0], y[0] get doubled-up
if x[0] != x[-1]:
x = np.concatenate((x, [x[0]]))
y = np.concatenate((y, [y[0]]))
line.set_data(x, y)
def set_varlabels(self, labels):
self.set_thetagrids(np.degrees(theta)) #labels
def _gen_axes_patch(self):
return self.draw_patch()
def _gen_axes_spines(self):
if frame == 'circle':
return PolarAxes._gen_axes_spines(self)
# The following is a hack to get the spines (i.e. the axes frame)
# to draw correctly for a polygon frame.
# spine_type must be 'left', 'right', 'top', 'bottom', or `circle`.
spine_type = 'circle'
verts = unit_poly_verts(theta)
# close off polygon by repeating first vertex
verts.append(verts[0])
path = Path(verts)
spine = Spine(self, spine_type, path)
spine.set_transform(self.transAxes)
return {'polar': spine}
register_projection(RadarAxes)
return theta
def unit_poly_verts(theta):
"""Return vertices of polygon for subplot axes.
This polygon is circumscribed by a unit circle centered at (0.5, 0.5)
"""
x0, y0, r = [0.5] * 3
verts = [(r*np.cos(t) + x0, r*np.sin(t) + y0) for t in theta]
return verts
def example_data():
# The following data is from the Denver Aerosol Sources and Health study.
# See doi:10.1016/j.atmosenv.2008.12.017
#
# The data are pollution source profile estimates for five modeled
# pollution sources (e.g., cars, wood-burning, etc) that emit 7-9 chemical
# species. The radar charts are experimented with here to see if we can
# nicely visualize how the modeled source profiles change across four
# scenarios:
# 1) No gas-phase species present, just seven particulate counts on
# Sulfate
# Nitrate
# Elemental Carbon (EC)
# Organic Carbon fraction 1 (OC)
# Organic Carbon fraction 2 (OC2)
# Organic Carbon fraction 3 (OC3)
# Pyrolized Organic Carbon (OP)
# 2)Inclusion of gas-phase specie carbon monoxide (CO)
# 3)Inclusion of gas-phase specie ozone (O3).
# 4)Inclusion of both gas-phase species is present...
data = [
['Sulfate', 'Nitrate', 'EC', 'OC1', 'OC2', 'OC3', 'OP', 'CO', 'O3'],
('Basecase', [
[0.88, 0.01, 0.03, 0.03, 0.00, 0.06, 0.01, 0.00, 0.00],
[0.07, 0.95, 0.04, 0.05, 0.00, 0.02, 0.01, 0.00, 0.00],
[0.01, 0.02, 0.85, 0.19, 0.05, 0.10, 0.00, 0.00, 0.00],
[0.02, 0.01, 0.07, 0.01, 0.21, 0.12, 0.98, 0.00, 0.00],
[0.01, 0.01, 0.02, 0.71, 0.74, 0.70, 0.00, 0.00, 0.00]]),
('With CO', [
[0.88, 0.02, 0.02, 0.02, 0.00, 0.05, 0.00, 0.05, 0.00],
[0.08, 0.94, 0.04, 0.02, 0.00, 0.01, 0.12, 0.04, 0.00],
[0.01, 0.01, 0.79, 0.10, 0.00, 0.05, 0.00, 0.31, 0.00],
[0.00, 0.02, 0.03, 0.38, 0.31, 0.31, 0.00, 0.59, 0.00],
[0.02, 0.02, 0.11, 0.47, 0.69, 0.58, 0.88, 0.00, 0.00]]),
('With O3', [
[0.89, 0.01, 0.07, 0.00, 0.00, 0.05, 0.00, 0.00, 0.03],
[0.07, 0.95, 0.05, 0.04, 0.00, 0.02, 0.12, 0.00, 0.00],
[0.01, 0.02, 0.86, 0.27, 0.16, 0.19, 0.00, 0.00, 0.00],
[0.01, 0.03, 0.00, 0.32, 0.29, 0.27, 0.00, 0.00, 0.95],
[0.02, 0.00, 0.03, 0.37, 0.56, 0.47, 0.87, 0.00, 0.00]]),
('CO & O3', [
[0.87, 0.01, 0.08, 0.00, 0.00, 0.04, 0.00, 0.00, 0.01],
[0.09, 0.95, 0.02, 0.03, 0.00, 0.01, 0.13, 0.06, 0.00],
[0.01, 0.02, 0.71, 0.24, 0.13, 0.16, 0.00, 0.50, 0.00],
[0.01, 0.03, 0.00, 0.28, 0.24, 0.23, 0.00, 0.44, 0.88],
[0.02, 0.00, 0.18, 0.45, 0.64, 0.55, 0.86, 0.00, 0.16]])
]
return data
if __name__ == '__main__':
N = 24
theta = radar_factory(N, frame='circle')
folder = glob.glob('/*.csv',recursive=True)
data = folder[0]
data = pd.read_csv(data)
unique_hours = data['Hour'].unique().reshape(1, -1)
unique_hours = unique_hours.tolist()
hourly_alarm = np.empty((24, 1))
for i in range(0, 24):
hourly_alarm[i] = data.loc[data['Hour'] == i, 'Number of Alarms'].sum()
hourly_alarm = hourly_alarm.reshape(1, -1)
hourly_alarm = hourly_alarm.tolist()
unique_hours[0].sort()
# data = example_data()
data = []
data.extend(unique_hours)
data.extend(hourly_alarm)
spoke_labels = data.pop(0)
fig, axes = plt.subplots(figsize=(9, 9), nrows=1, ncols=1,
subplot_kw=dict(projection='radar'))
#fig.subplots_adjust(wspace=0.25, hspace=0.20, top=0.85, bottom=0.05) #This line adjusts the amount of space between suplots. Not applicable here
colors = ['b', 'r', 'g', 'm', 'y']
# Plot the four cases from the example data on separate axes
axes.set_title('Blah', weight='bold', size='medium', position=(0.5, 1.1),
horizontalalignment='center', verticalalignment='center')
axes.scatter(theta, data[0])
This gives me a plot like this:
Here, I want to stagger the positions of the ylabels on the theta axis so that they do not appear in one line and do not look good.
Can I be pointed in the direction as to how to do this?
I tried to change the attributes of the PolarAxes class but it is not working.

Scipy Curve Fit Optimize not working for log scale values

So I am trying to fit a set of data points to this equation:
abs(I) = Io(exp((qV)/(nKT)) - 1) --- Shockley diode equation
to a bunch of data points I was given. Knowing the V and the I values, I need to optimize the Io and the n values to get me data closely matching the data set I was given.
However, scipy optimize curve fit is not giving me the values I want, which is where n = ~1.15 and Io = ~1.8E-13, and is instead giving me n = 2.12 and I = 2.11E-11. I suspect this is due to the data set values being very small numbers, messing with the optimization, but even when i set the initial guess to be n = 1.15 and Io = 1.8E-13, the optimization values do not change.
Does anyone have any tips on how to fix this?
import numpy as np
import math
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
Voltage = np.array([-0.5 , -0.49, -0.48, -0.47, -0.46, -0.45, -0.44, -0.43, -0.42,
-0.41, -0.4 , -0.39, -0.38, -0.37, -0.36, -0.35, -0.34, -0.33,
-0.32, -0.31, -0.3 , -0.29, -0.28, -0.27, -0.26, -0.25, -0.24,
-0.23, -0.22, -0.21, -0.2 , -0.19, -0.18, -0.17, -0.16, -0.15,
-0.14, -0.13, -0.12, -0.11, -0.1 , -0.09, -0.08, -0.07, -0.06,
-0.05, -0.04, -0.03, -0.02, -0.01, 0. , 0.01, 0.02, 0.03,
0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1 , 0.11, 0.12,
0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.2 , 0.21,
0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.3 ,
0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.4 ])
Current = np.array([ 6.99000000e-13, 6.83000000e-13, 6.57000000e-13,
6.46000000e-13, 6.19000000e-13, 6.07000000e-13,
5.86000000e-13, 5.73000000e-13, 5.55000000e-13,
5.37000000e-13, 5.27000000e-13, 5.08000000e-13,
4.92000000e-13, 4.75000000e-13, 4.61000000e-13,
4.43000000e-13, 4.32000000e-13, 4.18000000e-13,
3.99000000e-13, 3.91000000e-13, 3.79000000e-13,
3.66000000e-13, 3.54000000e-13, 3.43000000e-13,
3.34000000e-13, 3.18000000e-13, 3.06000000e-13,
2.96000000e-13, 2.86000000e-13, 2.77000000e-13,
2.66000000e-13, 2.59000000e-13, 2.54000000e-13,
2.43000000e-13, 2.33000000e-13, 2.22000000e-13,
2.16000000e-13, 2.07000000e-13, 2.00000000e-13,
1.94000000e-13, 1.85000000e-13, 1.77000000e-13,
1.68000000e-13, 1.58000000e-13, 1.48000000e-13,
1.35000000e-13, 1.21000000e-13, 1.03000000e-13,
7.53000000e-14, 4.32000000e-14, 2.33000000e-15,
6.46000000e-14, 1.57000000e-13, 2.82000000e-13,
4.58000000e-13, 7.07000000e-13, 1.06000000e-12,
1.57000000e-12, 2.28000000e-12, 3.29000000e-12,
4.75000000e-12, 6.80000000e-12, 9.76000000e-12,
1.39000000e-11, 1.82000000e-11, 2.57000000e-11,
3.67000000e-11, 5.21000000e-11, 7.39000000e-11,
1.04000000e-10, 1.62000000e-10, 2.27000000e-10,
3.21000000e-10, 4.48000000e-10, 6.21000000e-10,
8.70000000e-10, 1.20000000e-09, 1.66000000e-09,
2.27000000e-09, 3.08000000e-09, 4.13000000e-09,
5.46000000e-09, 7.05000000e-09, 8.85000000e-09,
1.11000000e-08, 1.39000000e-08, 1.74000000e-08,
2.05000000e-08, 2.28000000e-08, 2.52000000e-08,
2.91000000e-08])
def diode_function(V, n, Io):
kt = 300 * 1.38 * math.pow(10, -23)
q = 1.60 * math.pow(10, -19)
I_final = Io * (np.exp( (q * V) / (n * kt) ) - 1)
return abs(I_final)
p0 = [1.15, 1.8e-13]
popt, pcov = curve_fit(diode_function, Voltage, Current, p0 = p0)
print(popt)
fig = plt.figure()
ax = fig.add_subplot(121)
ax.set_title('I_d vs V_d')
ax.set_xlabel('V_d')
ax.set_ylabel('I_d')
ax.set_yscale('log')
plt.plot(Voltage, Current, 'ko', label="Original Data")
plt.plot(Voltage, diode_function(Voltage, *popt), 'r-', label="Fitted Curve")
plt.legend(loc='best')
ax = fig.add_subplot(122)
ax.set_title('I_d vs V_d')
ax.set_xlabel('V_d')
ax.set_ylabel('I_d')
ax.set_yscale('log')
popt = [1.15,1.8e-13]
plt.plot(Voltage, Current, 'ko', label="Original Data")
plt.plot(Voltage, diode_function(Voltage, *popt), 'r-', label="Fitted Curve")
plt.legend(loc='best')
plt.show()
Picture of the graph:
The left graph is with scipy optimization and the right graph is the one I want
I guess you are on the right track, using the logarithm to scale the data such that the differences are much lower. In order to prevent problems with logarithms, one usual option is to add a constant. Instead of log(x), one would use log(x+constant). This constant needs to be 1 or higher.
Using different constants still gives different results though, again because larger values are weighted higher in the least-squares method.
# imports and data as in question
def diode_function(V, n, Io):
kt = 300 * 1.38e-23
q = 1.60e-19
I_final = Io * (np.exp( (q * V) / (n * kt) ) - 1)
return np.abs(I_final)
p0 = [1.15, 1.8e-13]
popt, pcov = curve_fit(diode_function, Voltage, Current, p0 = p0)
fig, ax = plt.subplots()
ax.set_title('I_d vs V_d')
ax.set_xlabel('V_d')
ax.set_ylabel('I_d')
ax.set_yscale('log')
ax.plot(Voltage, Current, 'ko', label="Original Data")
offsets = [1,15]
colors = ["limegreen", "crimson"]
for offset, color in zip(offsets,colors):
logdf = lambda V,n,Io: np.log10(diode_function(V, n, Io)+offset)
poptn, pcovn = curve_fit(logdf, Voltage, np.log10(Current+offset), p0 = p0)
ax.plot(Voltage, 10**(logdf(Voltage, *poptn))-offset,
color=color, label="fit (offset: {})".format(offset))
ax.legend(loc='best')
plt.show()

Categories