2015-06-30 4 views
13

У меня есть файл, содержащий зарегистрированные события. Каждая запись имеет время и латентность. Я заинтересован в построении кумулятивной функции распределения задержек. Меня больше всего интересуют задержки хвоста, поэтому я хочу, чтобы график имел логарифмическую ось y. Меня интересуют задержки в следующих процентилях: 90-е, 99-е, 99,9-е, 99,99-е и 99,999-е. Вот мой код так далеко, что порождает регулярный CDF участок:Логарифмический график функции кумулятивного распределения в matplotlib

# 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() 

Regular CDF Plot

Я знаю, что я хочу сюжет выглядеть, но я изо всех сил, чтобы получить его. Я хочу, чтобы выглядеть следующим образом (я не генерировал этот сюжет):

Logarithmic CDF Plot

Заставить ось х логарифмическая проста. Ось Y - это проблема, которая дает мне проблемы. Использование set_yscale('log') не работает, потому что он хочет использовать полномочия 10. Я действительно хочу, чтобы ось y имела те же метки, что и этот график.

Как я могу получить свои данные в логарифмический сюжет, как этот?

EDIT:

Если я устанавливаю yscale в 'войти' и ylim на [0,1, 1], я получаю следующий сюжет:

enter image description here

Проблема заключается в том, что типичный логарифмическая шкала в наборе данных от 0 до 1 будет фокусироваться на значениях, близких к нулю. Вместо этого я хочу сфокусироваться на значениях, близких к 1.

+2

Какие проблемы у вас есть с 'set_yscale ('symlog')'? – mziccard

+0

Настройка позиций ярлыков - тоже совершенно другая история. Я полагаю, вы можете сделать масштаб логарифмическим по оси y (он работает, если у вас есть номер 0 или -ve, данные неверны), а затем смещать метки. –

+1

Что вы имеете в виду, когда говорите, что ось оси y * «не работает» *? Не могли бы вы показать нам? Математически невозможно представить 0 в масштабе шкалы, поэтому первое значение должно быть либо замаскировано, либо обрезано до очень небольшого положительного числа. Вы можете контролировать это поведение, передавая либо '' mask'', либо '' clip'' в качестве параметра 'nonposy =' в 'ax.set_yscale()'. –

ответ

14

По сути вы должны применить следующее преобразование ваших Y значений: -log10(1-y). Это накладывает единственное ограничение, которое y < 1, поэтому вы должны иметь отрицательные значения на преобразованном графике.

Вот модифицированный example из matplotlib документации, которая показывает, как включить пользовательские преобразования в «чешуйки»:

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() 

normal and transformed plot

Обратите внимания, что вы можете контролировать количество 9-й с помощью ключевого слова аргумента:

pylab.figure() 
pylab.plot(t) 
pylab.yscale('close_to_one', nines=3) 
pylab.grid(True) 

plot with 3 nine's

+0

отличный ответ. Это именно то, что я искал. Все работает как ожидалось, кроме одного ... Когда я пытаюсь использовать scatter() вместо plot(), он не работает (ничего не отображается). Что мне нужно сделать, чтобы разброс() работал? – nic

+0

@nic Как вы называете 'scatter()'? Все работает отлично для меня, если я просто заменю вызовы 'plot()' с помощью: 'pylab.scatter (t, t)'. –

+0

Вы правы. У меня была проблема в другом месте. Еще раз спасибо за ваш ответ. Это было хорошо стоит +100 – nic

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 в масштабе журнала, потому что у моих конкретных данных есть хорошая линейная линия без нее.

enter image description here

+2

Вы устанавливаете метки, но не тики, таким образом число, которое отображается (метка), не соответствует значению галочки !!! И почему бы вам просто не использовать параметр логарифмического масштабирования по умолчанию для matplotlib? – hitzg

+0

@hitzg, я согласен с вашим комментарием. Меня беспокоит, что эти метки не соответствуют фактическим данным. Я пробовал, пытался и пытался, но не могу понять, как заставить сюжет выглядеть как сюжет, который мне нужен без этого взлома. Я был бы очень благодарен, если бы вы могли показать мне, как это сделать! Логарифмическое масштабирование по умолчанию matplotlib по умолчанию не подчеркивает часть данных, о которых я забочусь, что является хвостовым процентилями. – nic