web-dev-qa-db-fra.com

Comment normaliser le distplot seaborn?

Pour des raisons de reproductibilité, l'ensemble de données et pour des raisons de reproductibilité, je le partage [ici] [1].

Voici ce que je fais - à partir de la colonne 2, je lis la ligne actuelle et la compare avec la valeur de la ligne précédente. S'il est plus grand, je continue de comparer. Si la valeur actuelle est inférieure à la valeur de la ligne précédente, je souhaite diviser la valeur actuelle (plus petite) par la valeur précédente (plus grande). En conséquence, le code suivant:

Cela donne les tracés suivants.

sns.distplot(quotient, hist=False, label=protname)

Comme nous pouvons le voir sur les parcelles

  • Data-V a un quotient de 0,8 lorsque le quotient_times est inférieur à 3 et le quotient reste 0,5 si quotient_times est supérieur à 3.

Je veux normaliser les valeurs pour que nous ayons y-axis des deuxièmes valeurs de tracé entre 0 et 1. Comment faire cela en Python?

2
Brown

Préface

D'après ce que je comprends, le distplot seaborn par défaut fait une estimation de kde. Si vous voulez un graphique distplot normalisé, cela peut être dû au fait que vous supposez que les Y du graphique doivent être délimités entre [0; 1]. Si c'est le cas, une question de débordement de pile a soulevé la question de estimateurs kde affichant des valeurs supérieures à 1 .

Citant ne réponse :

un pdf continu (pdf = fonction de densité de probabilité) ne dit jamais que la valeur est inférieure à 1, avec le pdf pour la variable aléatoire continue, f onction p(x) n'est pas la probabilité . vous pouvez vous référer à des variables aléatoires continues et à leurs distributions

Citant le premier commentaire de importanceofbeingernest :

L'intégrale sur un pdf est 1 . Il n'y a aucune contradiction à voir ici.

D'après mes connaissances, c'est la CDF (fonction de densité cumulative) dont les valeurs sont supposées être dans [0; 1].

Remarque: toutes les fonctions adaptables en continu possibles sont sur le site SciPy et disponibles dans le package scipy.stats

Peut-être aussi jeter un oeil à fonctions de masse de probabilité ?


Si vous voulez vraiment normaliser le même graphique, vous devez rassembler les points de données réels de la fonction tracée (Option1) ou la définition de la fonction (Option 2), les normaliser vous-même et les tracer à nouveau.

Option 1

enter image description here

import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import seaborn as sns
import sys

print('System versions          : {}'.format(sys.version))
print('System versions          : {}'.format(sys.version_info))
print('Numpy versqion           : {}'.format(np.__version__))
print('matplotlib.pyplot version: {}'.format(matplotlib.__version__))
print('seaborn version          : {}'.format(sns.__version__))

protocols = {}

types = {"data_v": "data_v.csv"}

for protname, fname in types.items():
    col_time,col_window = np.loadtxt(fname,delimiter=',').T
    trailing_window = col_window[:-1] # "past" values at a given index
    leading_window  = col_window[1:]  # "current values at a given index
    decreasing_inds = np.where(leading_window < trailing_window)[0]
    quotient = leading_window[decreasing_inds]/trailing_window[decreasing_inds]
    quotient_times = col_time[decreasing_inds]

    protocols[protname] = {
        "col_time": col_time,
        "col_window": col_window,
        "quotient_times": quotient_times,
        "quotient": quotient,
    }

    fig, (ax1, ax2) = plt.subplots(1,2, sharey=False, sharex=False)
    g = sns.distplot(quotient, hist=True, label=protname, ax=ax1, rug=True)
    ax1.set_title('basic distplot (kde=True)')
    # get distplot line points
    line = g.get_lines()[0]
    xd = line.get_xdata()
    yd = line.get_ydata()
    # https://stackoverflow.com/questions/29661574/normalize-numpy-array-columns-in-python
    def normalize(x):
        return (x - x.min(0)) / x.ptp(0)
    #normalize points
    yd2 = normalize(yd)
    # plot them in another graph
    ax2.plot(xd, yd2)
    ax2.set_title('basic distplot (kde=True)\nwith normalized y plot values')

    plt.show()

Option 2

Ci-dessous, j'ai essayé d'effectuer un kde et de normaliser l'estimation obtenue. Je ne suis pas un expert des statistiques, donc l'utilisation de kde peut être incorrecte d'une certaine manière (c'est différent de celui de seaborn comme on peut le voir sur la capture d'écran, c'est parce que seaborn fait le travail beaucoup mieux que moi. Il a seulement essayé d'imiter le montage de kde avec scipy. Le résultat n'est pas si mauvais que ça)

Capture d'écran:

enter image description here

Code:

import numpy as np
from scipy import stats
import matplotlib
import matplotlib.pyplot as plt
import seaborn as sns
import sys

print('System versions          : {}'.format(sys.version))
print('System versions          : {}'.format(sys.version_info))
print('Numpy versqion           : {}'.format(np.__version__))
print('matplotlib.pyplot version: {}'.format(matplotlib.__version__))
print('seaborn version          : {}'.format(sns.__version__))

protocols = {}

types = {"data_v": "data_v.csv"}

for protname, fname in types.items():
    col_time,col_window = np.loadtxt(fname,delimiter=',').T
    trailing_window = col_window[:-1] # "past" values at a given index
    leading_window  = col_window[1:]  # "current values at a given index
    decreasing_inds = np.where(leading_window < trailing_window)[0]
    quotient = leading_window[decreasing_inds]/trailing_window[decreasing_inds]
    quotient_times = col_time[decreasing_inds]

    protocols[protname] = {
        "col_time": col_time,
        "col_window": col_window,
        "quotient_times": quotient_times,
        "quotient": quotient,
    }

    fig, (ax1, ax2, ax3, ax4) = plt.subplots(1,4, sharey=False, sharex=False)
    diff=quotient_times
    ax1.plot(diff, quotient, ".", label=protname, color="blue")
    ax1.set_ylim(0, 1.0001)
    ax1.set_title(protname)
    ax1.set_xlabel("quotient_times")
    ax1.set_ylabel("quotient")
    ax1.legend()

    sns.distplot(quotient, hist=True, label=protname, ax=ax2, rug=True)
    ax2.set_title('basic distplot (kde=True)')

    # taken from seaborn's source code (utils.py and distributions.py)
    def seaborn_kde_support(data, bw, gridsize, cut, clip):
        if clip is None:
            clip = (-np.inf, np.inf)
        support_min = max(data.min() - bw * cut, clip[0])
        support_max = min(data.max() + bw * cut, clip[1])
        return np.linspace(support_min, support_max, gridsize)

    kde_estim = stats.gaussian_kde(quotient, bw_method='scott')

    # manual linearization of data
    #linearized = np.linspace(quotient.min(), quotient.max(), num=500)

    # or better: mimic seaborn's internal stuff
    bw = kde_estim.scotts_factor() * np.std(quotient)
    linearized = seaborn_kde_support(quotient, bw, 100, 3, None)

    # computes values of the estimated function on the estimated linearized inputs
    Z = kde_estim.evaluate(linearized)

    # https://stackoverflow.com/questions/29661574/normalize-numpy-array-columns-in-python
    def normalize(x):
        return (x - x.min(0)) / x.ptp(0)

    # normalize so it is between 0;1
    Z2 = normalize(Z)
    for name, func in {'min': np.min, 'max': np.max}.items():
        print('{}: source={}, normalized={}'.format(name, func(Z), func(Z2)))

    # plot is different from seaborns because not exact same method applied
    ax3.plot(linearized, Z, ".", label=protname, color="orange")
    ax3.set_title('Non linearized gaussian kde values')

    # manual kde result with Y axis avalues normalized (between 0;1)
    ax4.plot(linearized, Z2, ".", label=protname, color="green")
    ax4.set_title('Normalized gaussian kde values')

    plt.show()

Production:

System versions          : 3.7.2 (default, Feb 21 2019, 17:35:59) [MSC v.1915 64 bit (AMD64)]
System versions          : sys.version_info(major=3, minor=7, micro=2, releaselevel='final', serial=0)
Numpy versqion           : 1.16.2
matplotlib.pyplot version: 3.0.2
seaborn version          : 0.9.0
min: source=0.0021601491646143518, normalized=0.0
max: source=9.67319154426489, normalized=1.0

Contrairement à un commentaire, en traçant:

[(x-min(quotient))/(max(quotient)-min(quotient)) for x in quotient]

Ne change pas le comportement! Il modifie uniquement les données source pour l'estimation de la densité du noyau. La forme de la courbe resterait la même.

Citant le document distplot de seaborn :

Cette fonction combine la fonction matplotlib hist (avec calcul automatique d'une bonne taille de bac par défaut) avec les fonctions seaborn kdeplot () et rugplot (). Il peut également s'adapter aux distributions scipy.stats et tracer le PDF estimé sur les données.

Par défaut:

kde: bool, optionnel défini sur True Indique s'il faut tracer une estimation de la densité du noyau gaussien.

Il utilise kde par défaut. Citant le doc kde de Seaborn:

Ajustez et tracez une estimation de densité de noyau univariée ou bivariée.

Citation doc méthode ki gaussienne SCiPy :

Représentation d'une estimation de densité de noyau à l'aide de noyaux gaussiens.

L'estimation de la densité du noyau est un moyen d'estimer la fonction de densité de probabilité (PDF) d'une variable aléatoire de manière non paramétrique. gaussian_kde fonctionne pour les données à la fois univariées et multivariées. Il comprend la détermination automatique de la bande passante. L'estimation fonctionne mieux pour une distribution unimodale; les distributions bimodales ou multimodales ont tendance à être trop lissées.

Notez que je pense que vos données sont bimodales, comme vous l'avez mentionné vous-même. Ils ont également l'air discrets. Pour autant que je sache, la fonction de distribution discrète peut ne pas être analysée de la même manière que la fonction continue, et l'ajustement peut s'avérer délicat.

Voici un exemple avec différentes lois:

import numpy as np
from scipy.stats import uniform, powerlaw, logistic
import matplotlib
import matplotlib.pyplot as plt
import seaborn as sns
import sys

print('System versions          : {}'.format(sys.version))
print('System versions          : {}'.format(sys.version_info))
print('Numpy versqion           : {}'.format(np.__version__))
print('matplotlib.pyplot version: {}'.format(matplotlib.__version__))
print('seaborn version          : {}'.format(sns.__version__))

protocols = {}

types = {"data_v": "data_v.csv"}

for protname, fname in types.items():
    col_time,col_window = np.loadtxt(fname,delimiter=',').T
    trailing_window = col_window[:-1] # "past" values at a given index
    leading_window  = col_window[1:]  # "current values at a given index
    decreasing_inds = np.where(leading_window < trailing_window)[0]
    quotient = leading_window[decreasing_inds]/trailing_window[decreasing_inds]
    quotient_times = col_time[decreasing_inds]

    protocols[protname] = {
        "col_time": col_time,
        "col_window": col_window,
        "quotient_times": quotient_times,
        "quotient": quotient,
    }
    fig, [(ax1, ax2, ax3), (ax4, ax5, ax6)] = plt.subplots(2,3, sharey=False, sharex=False)
    diff=quotient_times
    ax1.plot(diff, quotient, ".", label=protname, color="blue")
    ax1.set_ylim(0, 1.0001)
    ax1.set_title(protname)
    ax1.set_xlabel("quotient_times")
    ax1.set_ylabel("quotient")
    ax1.legend()
    quotient2 = [(x-min(quotient))/(max(quotient)-min(quotient)) for x in quotient]
    print(quotient2)
    sns.distplot(quotient, hist=True, label=protname, ax=ax2, rug=True)
    ax2.set_title('basic distplot (kde=True)')
    sns.distplot(quotient2, hist=True, label=protname, ax=ax3, rug=True)
    ax3.set_title('logistic distplot')

    sns.distplot(quotient, hist=True, label=protname, ax=ax4, rug=True, kde=False, fit=uniform)
    ax4.set_title('uniform distplot')
    sns.distplot(quotient, hist=True, label=protname, ax=ax5, rug=True, kde=False, fit=powerlaw)
    ax5.set_title('powerlaw distplot')
    sns.distplot(quotient, hist=True, label=protname, ax=ax6, rug=True, kde=False, fit=logistic)
    ax6.set_title('logistic distplot')
    plt.show()

Production:

System versions          : 3.7.2 (default, Feb 21 2019, 17:35:59) [MSC v.1915 64 bit (AMD64)]
System versions          : sys.version_info(major=3, minor=7, micro=2, releaselevel='final', serial=0)
Numpy versqion           : 1.16.2
matplotlib.pyplot version: 3.0.2
seaborn version          : 0.9.0
[1.0, 0.05230125523012544, 0.0433775382360589, 0.024590765616971128, 0.05230125523012544, 0.05230125523012544, 0.05230125523012544, 0.02836946874603772, 0.05230125523012544, 0.05230125523012544, 0.05230125523012544, 0.05230125523012544, 0.03393500048652319, 0.05230125523012544, 0.05230125523012544, 0.05230125523012544, 0.0037013196009011043, 0.0, 0.05230125523012544]

Capture d'écran:

enter image description here

6
LoneWanderer