2015-06-30 17 views
13

Ich habe eine Datei mit protokollierten Ereignissen. Jeder Eintrag hat eine Zeit und Latenz. Ich bin interessiert, die kumulative Verteilungsfunktion der Latenzen zu zeichnen. Ich bin am meisten an Tail-Latenzen interessiert, deshalb möchte ich, dass der Plot eine logarithmische Y-Achse hat. Ich bin an den Latenzen bei den folgenden Perzentilen interessiert: 90., 99., 99., 99. und 99.999. Hier ist mein Code so weit, dass ein regelmäßigen CDF Plot erzeugt:Logarithmische Darstellung einer kumulativen Verteilungsfunktion in 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

Ich weiß, was ich die Handlung aussehen soll, aber ich habe gekämpft, um es zu bekommen. Ich mag es so aussehen (ich habe dieses Grundstück nicht erzeugen):

Logarithmic CDF Plot

machte die x-Achse logarithmisch einfach ist. Die y-Achse ist diejenige, die mir Probleme bereitet. Die Verwendung von set_yscale('log') funktioniert nicht, weil es Potenzen von 10 verwenden möchte. Ich möchte wirklich, dass die Y-Achse die gleichen Ticklabels wie dieses Diagramm hat.

Wie kann ich meine Daten in eine logarithmische Darstellung wie diese bringen?

EDIT:

Wenn ich die yscale zu 'log' und ylim auf [0,1, 1], erhalte ich die folgende Handlung:

enter image description here

Das Problem ist, dass ein typisches Log-Scale-Plot auf einem Datensatz von 0 bis 1 konzentriert sich auf Werte nahe Null. Stattdessen möchte ich auf die Werte in der Nähe von 1 konzentrieren.

+2

Welche Probleme haben Sie mit 'set_yscale ('symlog')'? – mziccard

+0

Das Setzen von Etikettenpositionen ist auch eine ganz andere Geschichte. Ich nehme an, Sie könnten die Skalierung logarithmisch auf der y-Achse machen (es funktioniert, wenn Sie eine 0 oder -ve-Zahl haben, die Daten sind falsch) und dann die Beschriftungen adjustieren. –

+1

Was meinen Sie, wenn Sie sagen, dass die Log-Y-Achse * "nicht funktioniert" *? Kannst du es uns zeigen? Es ist mathematisch nicht möglich, 0 auf einer logarithmischen Skala darzustellen, so dass der erste Wert entweder maskiert oder auf eine sehr kleine positive Zahl abgeschnitten werden muss. Sie können dieses Verhalten steuern, indem Sie entweder '' mask '' oder '' clip '' als 'nonposy = 'Parameter an' ax.set_yscale() 'übergeben. –

Antwort

14

Im Wesentlichen müssen Sie die folgende Transformation auf Ihre Y Werte anzuwenden: -log10(1-y). Dies stellt die einzige Einschränkung dar, dass y < 1, so sollten Sie in der Lage sein, negative Werte auf dem transformierten Diagramm haben.

Hier ist eine modifizierte example von matplotlib Dokumentation, die zeigt, wie benutzerdefinierte Transformationen in die „Waage“ zu übernehmen:

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

Beachten Sie, dass die Anzahl der 9er über ein Schlüsselwort-Argument steuern:

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

plot with 3 nine's

+0

große Antwort. Genau das habe ich gesucht. Alles funktioniert wie erwartet bis auf eine Sache ... Wenn ich streat() anstelle von plot() verwenden möchte, funktioniert es nicht (nichts wird angezeigt). Was muss ich tun, damit scatter() funktioniert? – nic

+0

@nic Wie nennt man 'scatter()'? Alles funktioniert gut für mich, wenn ich nur die 'plot()' Aufrufe mit: 'pylab.scatter (t, t)' ersetze. –

+0

Sie haben Recht. Ich hatte anderswo ein Problem. Danke nochmal für deine Antwort. Es war es wert +100 – nic

1

Ok, das ist nicht der sauberste Code, aber ich sehe keinen Weg um ihn herum. Vielleicht ist das, wonach ich wirklich frage, keine logarithmische CDF, aber ich warte darauf, dass ein Statistiker mir etwas anderes sagt. Wie auch immer, hier ist, was ich gefunden habe:

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

Der unordentliche Teil ist, wo ich die yticklabels ändern. Die Variable wird Werte zwischen 0 und 10 enthalten, und in meinem Beispiel war es zwischen 0 und 6. In diesem Code tausche ich die Etiketten mit Perzentilen. Die plot Funktion könnte auch verwendet werden, aber ich mag die Art und Weise, wie die scatter Funktion die Ausreißer auf dem Schwanz zeigt. Außerdem wähle ich, die X-Achse nicht auf einer logarithmischen Skala zu machen, weil meine speziellen Daten eine gute lineare Linie ohne sie haben.

enter image description here

+2

Sie setzen die Labels, aber nicht die Ticks, so dass die angezeigte Nummer (Label) nicht dem Wert des Ticks entspricht !!! Und warum würden Sie nicht einfach die standardmäßige logarithmische Skalierungsoption von matplotlib verwenden? – hitzg

+0

@hitzg, ich stimme deinem Kommentar zu. Es stört mich, dass die Etiketten nicht mit den tatsächlichen Daten übereinstimmen. Ich habe es versucht und versucht und versucht, aber ich kann nicht herausfinden, wie ich die Handlung so aussehen lassen kann, wie die Handlung, die ich ohne diesen Hack brauche. Ich wäre SEHR dankbar, wenn du mir das zeigen könntest! Die standardmäßige logarithmische Skalierung von Matplotlib betont nicht den Teil der Daten, der mir wichtig ist, nämlich die Schwanzperzentile. – nic