我不知道我究竟想要做什么。我也是Python的新用户。基本上我创建了一个非常简单的类,用于表示形式的测量量
并自动执行错误传播。这是定义:
from math import sqrt
from math import log10
from math import fabs
from math import floor
from math import ceil
from math import pi
class magnitude:
def __init__(self, val, err):
self.val = val
self.err = err
def __str__(self):
"Prints the magnitude in the format v.vvvv(v+-e) being 'v' the relevant digits of the magnitude value and '+-e' the most significant digit of the error."
if self.err == 0 or self.val == 0:
return str(self.val)
if self.err >= fabs(self.val):
return '{:.0e} +- {:.0e}'.format(self.val, self.err) + ' (Infinite uncertainty!)'
else:
temp = '{:.' + str(ceil(log10(fabs(self.val*(1+pi*1e-3))) - ceil(log10(self.err*(1+pi*1e-3))) )) + 'e}' # Calculates number of digits for the representative part. The adition of 'pi*1e-3' is to add some noise and avoid error values of '1', '10', '100', ..., because they are not well printed.
temp = temp.format(self.val)
temp = temp[:-4] # Remove last 4 chars.
return temp + '(+-' + str(int(self.err*10**(ceil(-log10(self.err))))) + ')' + '{:.0e}'.format(fabs(self.val))[1:] + ' ({:d} ppm)'.format(int(self.err/fabs(self.val)*1e6))
# --------------------
def __add__(self, other):
if type(other) == int or type(other) == float:
other = magnitude(other, 0)
return magnitude(self.val + other.val, sqrt(self.err**2 + other.err**2))
def __radd__(self, other):
if type(other) == int or type(other) == float:
other = magnitude(other, 0)
return magnitude(self.val + other.val, sqrt(self.err**2 + other.err**2))
# --------------------
def __sub__(self, other):
if type(other) == int or type(other) == float:
other = magnitude(other, 0)
return magnitude(self.val - other.val, sqrt(self.err**2 + other.err**2))
def __rsub__(self, other):
if type(other) == int or type(other) == float:
other = magnitude(other, 0)
return magnitude(-self.val + other.val, sqrt(self.err**2 + other.err**2))
# --------------------
def __mul__(self, other):
if type(other) == int or type(other) == float:
other = magnitude(other, 0)
return magnitude(self.val*other.val, sqrt(self.val**2*other.err**2 + self .err**2*other.val**2))
def __rmul__(self, other):
if type(other) == int or type(other) == float:
other = magnitude(other, 0)
return magnitude(self.val*other.val, sqrt(self.val**2*other.err**2 + self .err**2*other.val**2))
# --------------------
def __truediv__(self, other):
if type(other) == int or type(other) == float:
other = magnitude(other, 0)
return magnitude(self.val/other.val, sqrt(self.err**2/other.val**2 + self.val**2/other.val**2*other.err**2))
def __rtruediv__(self, other):
if type(other) == int or type(other) == float:
other = magnitude(other, 0)
return magnitude(other.val/self.val, sqrt(other.err**2/self.val**2 + other.val**2/self.val**2*self.err**2))
现在我想要的是重载matplotlib绘图函数,以便告诉它应该如何绘制,即,使用magnitude.val值的粗线和围绕它的两条细线,幅度为.err。< / p>
我该怎么做?
答案 0 :(得分:1)
我不知道在matplotlib中重载绘图函数的方法。但是,您可以创建一个模拟matplotlib绘图函数的新函数。
def plot(x=None, y=None, *args, axis=None, **kwargs):
"""Creates a plot from iterable, works with magnitude instances.
All keyword arguments in matplotlib functions are accepted,
but `linewidth` should not be used if y contains magnitude instances.
Parameters
----------
x : Array like
The x positions of the plot [pomts. If y is not given, y is set to x and
x is set to range(len(y)).
y : Array like
The y positions of the plot points.
axis : matplotlib.Axes (optional)
Axes to plot the values in.
"""
if axis is None:
axis = plt
if y is None:
y = x
x = range(len(y))
if isinstance(y[0], magnitude):
for yi in y:
if not isinstance(y[i]), magnitude):
raise ValueError(
'Either all or none of the plot positions should '
'be magnitude instances'
)
y_val = [yi.val for yi in y]
y_err = [yi.err for yi in y]
lines = axis.plot(x, y_val, *args, linewidth=2.0, **kwargs)
lines += axis.plot(x, y_val + y_err, *args, **kwargs)
lines += axis.plot(x, y_val - y_err, *args, **kwargs)
else:
lines = axis.plot(x, y, *args, **kwargs)
return lines
最后,我建议在错误行之间使用填充,而不是粗细和两条细线。
答案 1 :(得分:1)
感谢Yngve Moe的回答,我设法编写了这个函数
def plot_yerr_filled(x=None, y=None, axis=plt, color=(0,0,0), alpha=0.25, *args, **kwargs):
"""Creates a 'filled error bar' plot. All keyword arguments in matplotlib functions are accepted.
Parameters
----------
x : Array like (list of float, int, etc. or numpy.array or list of magnitude) of exact 'x' values (i.e. with no error, if 'x' is a list of 'magnitude' type objects, errors are discarded).
y : Array like (list) of 'magnitude' type elements containing the 'y' values and the 'yerr' associated error.
axis: matplotlib.axes (optional) in which to plot the data.
color: tuple (r,g,b) defining the color.
alpha: float between 0 and 1 indicating the transparency of the filled error bar.
"""
for i in range(0, len(x)): # Validation of x
if isinstance(x[i], magnitude):
x[i] = x[i].val
for i in range(len(y)): # Validation of y
if not isinstance(y[i], magnitude):
raise ValueError('y[' + str(i) + '] is not of type "magnitude"')
y_val = [y[i].val for i in range(len(y))]
y_err = [y[i].err for i in range(len(y))]
axis.fill_between(np.array(x), np.array(y_val)-np.array(y_err), np.array(y_val) + np.array(y_err), alpha=alpha, edgecolor=color, facecolor=color )
lines = axis.plot(np.array(x), np.array(y_val), color=color, *args, **kwargs)
return lines
完全符合我的要求。我在这里举一个例子
import pymagpy as mg
import matplotlib.pyplot as plt
plt.rc('axes', axisbelow=True) # This is for plotting data in front of the grid.
plt.rcParams.update({'font.size': 8}) # Changes the default font size for the plots.
import numpy as np
x = mg.magnitude(1, 0.1)
y = mg.magnitude(-6798, 6)
z = mg.magnitude(1e-30, .00002872982778297289e-30)
print(x)
print(y)
print(z)
print(x+y/z*2-z/x*y) # Automatically calculates the error propagation.
x_values = []
y_values = []
z_values = []
for k in range(10):
x_values.append(mg.magnitude(k, k**2))
y_values.append(mg.magnitude(k**2, k))
z_values.append(mg.magnitude(np.sqrt(300*k)+1, k/(k-2.2)+5))
FIG_WIDTH = 130e-3 # Figure width in meters (size of the chart+axis_ticks, not counting legend, title, etc.).
FIG_RATIO = [1, 0.8] # XY ratio aspect of figures.
f, (ax1, ax2) = plt.subplots(2, sharex=True, figsize=(FIG_WIDTH*FIG_RATIO[0]/25.4e-3, FIG_WIDTH*FIG_RATIO[1]/25.4e-3)) # Create the figure for plotting.
f.subplots_adjust(hspace=0.1) # Fine-tune figure; make subplots close to each other and hide x ticks for all but bottom plot.
mg.plot_yerr_filled(x_values, y_values, color=(1,0,0), alpha=.1, axis=ax1, label='Y', marker='.')
mg.plot_yerr_filled(x_values, z_values, color=(0,0,1), axis=ax1, label='Z', marker='o', linestyle='')
ax1.set_ylabel('y axis')
ax1.grid(b=True, which='major', color='#aaaaaa', linestyle='-', linewidth=0.5) # Configure the grid in a more fachera way.
ax1.grid(b=True, which='minor', color='#dddddd', linestyle='-', linewidth=0.25) # Configure the grid in a more fachera way.
ax1.minorticks_on() # Enables minor ticks without text, only the ticks.
ax1.legend(loc='best', fancybox=True, framealpha=0.7, ncol=1, fontsize=8)
mg.plot_yerr_filled(x_values, [(a*b)/(a+b) for a,b in zip(y_values, z_values)], axis=ax2, label='(Y*Z)/(Y+Z)', marker='.', linestyle='--')
ax2.set_xlabel('x axis')
ax2.set_ylabel('y axis')
ax2.grid(b=True, which='major', color='#aaaaaa', linestyle='-', linewidth=0.5) # Configure the grid in a more fachera way.
ax2.grid(b=True, which='minor', color='#dddddd', linestyle='-', linewidth=0.25) # Configure the grid in a more fachera way.
ax2.minorticks_on() # Enables minor ticks without text, only the ticks.
ax2.legend(loc='best', fancybox=True, framealpha=0.7, ncol=1, fontsize=8)
plt.savefig('example.pdf', bbox_inches='tight')
plt.show()
产生这个情节: