我有一个包含已记录事件的文件。每个条目都有时间和延迟。我有兴趣绘制延迟的累积分布函数。我对尾部延迟最感兴趣所以我希望绘图具有对数y轴。我对以下百分位数的延迟感兴趣:第90,99,99.9,99.99和99.999。到目前为止,我的代码是生成常规CDF图:
# retrieve event times and latencies from the file
times, latencies = read_in_data_from_file('myfile.csv')
# compute the CDF
cdfx = numpy.sort(latencies)
cdfy = numpy.linspace(1 / len(latencies), 1.0, len(latencies))
# plot the CDF
plt.plot(cdfx, cdfy)
plt.show()
我知道我希望情节看起来像什么,但我很难得到它。我希望它看起来像这样(我没有生成这个情节):
使x轴对数很简单。 y轴是给我带来问题的那个。使用set_yscale('log')
不起作用,因为它想要使用10的幂。我真的希望y轴具有与该图相同的标记标记。
如何将我的数据放入像这样的对数图中?
编辑:
如果我将yscale设置为'log',并将ylim设置为[0.1,1],我会得到以下图:
问题是数据集上从0到1的典型对数刻度图将集中于接近零的值。相反,我想专注于接近1的值。
答案 0 :(得分:15)
基本上,您需要将以下转换应用于Y
值:-log10(1-y)
。这强加了y < 1
的唯一限制,因此您应该能够在转换后的图上具有负值。
以下是来自matplotlib
文档的经过修改的example,其中显示了如何将自定义转换合并到&#34; scale&#34;:
import numpy as np
from numpy import ma
from matplotlib import scale as mscale
from matplotlib import transforms as mtransforms
from matplotlib.ticker import FixedFormatter, FixedLocator
class CloseToOne(mscale.ScaleBase):
name = 'close_to_one'
def __init__(self, axis, **kwargs):
mscale.ScaleBase.__init__(self)
self.nines = kwargs.get('nines', 5)
def get_transform(self):
return self.Transform(self.nines)
def set_default_locators_and_formatters(self, axis):
axis.set_major_locator(FixedLocator(
np.array([1-10**(-k) for k in range(1+self.nines)])))
axis.set_major_formatter(FixedFormatter(
[str(1-10**(-k)) for k in range(1+self.nines)]))
def limit_range_for_scale(self, vmin, vmax, minpos):
return vmin, min(1 - 10**(-self.nines), vmax)
class Transform(mtransforms.Transform):
input_dims = 1
output_dims = 1
is_separable = True
def __init__(self, nines):
mtransforms.Transform.__init__(self)
self.nines = nines
def transform_non_affine(self, a):
masked = ma.masked_where(a > 1-10**(-1-self.nines), a)
if masked.mask.any():
return -ma.log10(1-a)
else:
return -np.log10(1-a)
def inverted(self):
return CloseToOne.InvertedTransform(self.nines)
class InvertedTransform(mtransforms.Transform):
input_dims = 1
output_dims = 1
is_separable = True
def __init__(self, nines):
mtransforms.Transform.__init__(self)
self.nines = nines
def transform_non_affine(self, a):
return 1. - 10**(-a)
def inverted(self):
return CloseToOne.Transform(self.nines)
mscale.register_scale(CloseToOne)
if __name__ == '__main__':
import pylab
pylab.figure(figsize=(20, 9))
t = np.arange(-0.5, 1, 0.00001)
pylab.subplot(121)
pylab.plot(t)
pylab.subplot(122)
pylab.plot(t)
pylab.yscale('close_to_one')
pylab.grid(True)
pylab.show()
请注意,您可以通过关键字参数控制9的数量:
pylab.figure()
pylab.plot(t)
pylab.yscale('close_to_one', nines=3)
pylab.grid(True)
答案 1 :(得分:1)
好的,这不是最干净的代码,但我看不到它的解决方法。也许我真正要求的不是对数CDF,但我会等一位统计学家告诉我。无论如何,这是我想出的:
# retrieve event times and latencies from the file
times, latencies = read_in_data_from_file('myfile.csv')
cdfx = numpy.sort(latencies)
cdfy = numpy.linspace(1 / len(latencies), 1.0, len(latencies))
# find the logarithmic CDF and ylabels
logcdfy = [-math.log10(1.0 - (float(idx) / len(latencies)))
for idx in range(len(latencies))]
labels = ['', '90', '99', '99.9', '99.99', '99.999', '99.9999', '99.99999']
labels = labels[0:math.ceil(max(logcdfy))+1]
# plot the logarithmic CDF
fig = plt.figure()
axes = fig.add_subplot(1, 1, 1)
axes.scatter(cdfx, logcdfy, s=4, linewidths=0)
axes.set_xlim(min(latencies), max(latencies) * 1.01)
axes.set_ylim(0, math.ceil(max(logcdfy)))
axes.set_yticklabels(labels)
plt.show()
凌乱的部分是我改变yticklabels的地方。 logcdfy
变量将保存0到10之间的值,在我的示例中,它介于0和6之间。在此代码中,我将标签与百分位数交换。也可以使用plot
函数,但我喜欢scatter
函数显示尾部异常值的方式。另外,我选择不在对数刻度上制作x轴,因为我的特定数据在没有它的情况下具有良好的线性线。