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);
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);
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')
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.
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
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
È 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