2015-01-30 8 views
5

Seaborn è un ottimo pacchetto per eseguire alcune operazioni di alto livello con output ottimali. Tuttavia, sto lottando un po 'con l'utilizzo di Seaborn per sovrapporre i dati e le previsioni del modello da un modello esternamente adatto. In questo esempio sto adattando modelli in Statsmodels che sono troppo complessi per Seaborn per fare out-of-the-box, ma penso che il problema sia più generale (cioè se ho delle previsioni sui modelli e voglio visualizzare sia loro che i dati usando Seaborn).Visualizzazione dei dati e delle previsioni del modello in un grafico utilizzando Seaborn e Statsmodels

Cominciamo con le importazioni e un set di dati:

import numpy as np 
import pandas as pd 
import seaborn as sns 
import statsmodels.formula.api as smf 
import patsy 
import itertools 
import matplotlib.pyplot as plt 

np.random.seed(12345) 

# make a data frame with one continuous and two categorical variables: 
df = pd.DataFrame({'x1': np.random.normal(size=100), 
        'x2': np.tile(np.array(['a', 'b']), 50), 
        'x3': np.repeat(np.array(['c', 'd']), 50)}) 

# create a design matrix using patsy: 
X = patsy.dmatrix('x1 * x2 * x3', df) 

# some random beta weights: 
betas = np.random.normal(size=X.shape[1]) 

# create the response variable as the noisy linear combination of predictors: 
df['y'] = np.inner(X, betas) + np.random.normal(size=100) 

Allestiamo un modello in statsmodels contenenti tutte le variabili predittive e le loro interazioni:

# fit a model with all interactions 
fit = smf.ols('y ~ x1 * x2 * x3', df).fit() 
print(fit.summary()) 

Dato che in questo caso abbiamo tutte le combinazioni di variabili specificato, e le nostre previsioni del modello sono lineari, sarebbe sufficiente per il grafico aggiungere una nuova colonna "predizioni" al dataframe contenente le previsioni del modello. Tuttavia, questo non è molto generale (immaginate il nostro modello non è lineare e quindi vogliamo che i nostri complotti per mostrare curve morbide), così invece io faccio un nuovo dataframe con tutte le combinazioni di predittori, quindi generare previsioni:

# create a new dataframe of predictions, using pandas' expand grid: 
def expand_grid(data_dict): 
    """ A port of R's expand.grid function for use with Pandas dataframes. 

    from http://pandas.pydata.org/pandas-docs/stable/cookbook.html?highlight=expand%20grid 

    """ 
    rows = itertools.product(*data_dict.values()) 
    return pd.DataFrame.from_records(rows, columns=data_dict.keys()) 


# build a new matrix with expand grid: 

preds = expand_grid(
       {'x1': np.linspace(df['x1'].min(), df['x1'].max(), 2), 
       'x2': ['a', 'b'], 
       'x3': ['c', 'd']}) 
preds['yhat'] = fit.predict(preds) 

Il preds dataframe assomiglia a questo:

x3  x1 x2  yhat 
0 c -2.370232 a -1.555902 
1 c -2.370232 b -2.307295 
2 c 3.248944 a -1.555902 
3 c 3.248944 b -2.307295 
4 d -2.370232 a -1.609652 
5 d -2.370232 b -2.837075 
6 d 3.248944 a -1.609652 
7 d 3.248944 b -2.837075 

Dal comandi plot Seaborn (a differenza ggplot2 comandi in R) sembrano accettare una e una sola dataframe, abbiamo bisogno di unire le nostre previsioni in dati grezzi:

# append to df: 
merged = df.append(preds) 

Ora possiamo tracciare le previsioni dei modelli insieme ai dati, con il nostro continuo variabile x1 come l'asse x:

# plot using seaborn: 
sns.set_style('white') 
sns.set_context('talk') 
g = sns.FacetGrid(merged, hue='x2', col='x3', size=5) 
# use the `map` method to add stuff to the facetgrid axes: 
g.map(plt.plot, "x1", "yhat") 
g.map(plt.scatter, "x1", "y") 
g.add_legend() 
g.fig.subplots_adjust(wspace=0.3) 
sns.despine(offset=10); 

enter image description here

Fin qui tutto bene. Ora immagina di non aver misurato la variabile continua x1 e sappiamo solo delle altre due variabili (categoriali) (ad esempio, abbiamo un disegno fattoriale 2x2). Come possiamo tracciare le previsioni del modello rispetto ai dati in questo caso?

fit = smf.ols('y ~ x2 * x3', df).fit() 
print(fit.summary()) 

preds = expand_grid(
       {'x2': ['a', 'b'], 
       'x3': ['c', 'd']}) 
preds['yhat'] = fit.predict(preds) 
print(preds) 

# append to df: 
merged = df.append(preds) 

Beh, siamo in grado di tracciare le previsioni del modello utilizzando sns.pointplot o simili, in questo modo:

# plot using seaborn: 
g = sns.FacetGrid(merged, hue='x3', size=4) 
g.map(sns.pointplot, 'x2', 'yhat') 
g.add_legend(); 
sns.despine(offset=10); 

enter image description here

O i dati utilizzando sns.factorplot in questo modo:

g = sns.factorplot('x2', 'y', hue='x3', kind='point', data=merged) 
sns.despine(offset=10); 
g.savefig('tmp3.png') 

enter image description here

Ma non vedo come produrre un grafico simile al primo (ad es. linee per le previsioni del modello utilizzando plt.plot, una dispersione di punti per i dati che utilizzano plt.scatter). Il motivo è che la variabile x2 che sto cercando di usare come asse x è una stringa/oggetto, quindi i comandi pyplot non sanno cosa fare con essi.

+0

Nota che riconosco che le linee nell'ultimo grafico sono uguali alle righe nel secondo grafico (ad es. le previsioni del modello sono solo linee tra i mezzi). Questo non sarà sempre vero, quindi, sto cercando un approccio più generale. – tsawallis

+0

Si noti inoltre che, per qualche motivo sconosciuto, la legenda nel secondo grafico non mostra i casi "c" e "d", ma solo il titolo della legenda. Non so perché. – tsawallis

+0

È possibile passare qualsiasi funzione a "FacetGrid.map" a condizione che utilizzi gli argomenti posizionali 'x',' y' sugli assi "correntemente" attivi. Quindi dovresti essere in grado di definire una funzione che mappa dalle tue categorie a [0, 1, 2, ...] e usarla. Questo aiuta? – mwaskom

risposta

4

Come ho detto nei miei commenti, ci sono due modi in cui vorrei pensare a farlo.

Il primo è quello di definire una funzione che fa la forma e poi trame e passarlo al FacetGrid.map:

import pandas as pd 
import seaborn as sns 
tips = sns.load_dataset("tips") 

def plot_good_tip(day, total_bill, **kws): 

    expected_tip = (total_bill.groupby(day) 
           .mean() 
           .apply(lambda x: x * .2) 
           .reset_index(name="tip")) 
    sns.pointplot(expected_tip.day, expected_tip.tip, 
        linestyles=["--"], markers=["D"]) 

g = sns.FacetGrid(tips, col="sex", size=5) 
g.map(sns.pointplot, "day", "tip") 
g.map(plot_good_tip, "day", "total_bill") 
g.set_axis_labels("day", "tip") 

enter image description here

La seconda è il calcolo dei valori previsti e poi li si fondono nella vostra dataframe con una variabile aggiuntiva che identifica che cosa è dati e cosa è il modello:

tip_predict = (tips.groupby(["day", "sex"]) 
        .total_bill 
        .mean() 
        .apply(lambda x: x * .2) 
        .reset_index(name="tip")) 
tip_all = pd.concat(dict(data=tips[["day", "sex", "tip"]], model=tip_predict), 
        names=["kind"]).reset_index() 

sns.factorplot("day", "tip", "kind", data=tip_all, col="sex", 
       kind="point", linestyles=["-", "--"], markers=["o", "D"]) 

enter image description here

Problemi correlati