2015-01-30 10 views
5

Seaborn to świetny pakiet do robienia wysokiej jakości kreślenia z ładnymi wyjściami. Trochę jednak borykam się z używaniem Seaborn do nakładania zarówno danych, jak i prognoz modeli z modelu dopasowanego zewnętrznie. W tym przykładzie dopasowuję modele w Statsmodels, które są zbyt skomplikowane, aby Seaborn robił coś poza opakowaniem, ale myślę, że problem jest bardziej ogólny (tj. Jeśli mam przewidywania modelu i chcę wizualizować zarówno je, jak i dane za pomocą Seaborn).Wyświetlanie prognoz danych i modeli na jednym wykresie za pomocą Seaborn i Statsmodels

Zacznijmy od importu i zestaw danych:

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) 

Mamy dopasować model w statsmodels zawierających wszystkie zmienne predykcyjne i ich interakcje:

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

Ponieważ w tym przypadku mamy wszystkie kombinacje zmiennych określone, a nasze prognozy modelu są liniowe, wystarczyłoby, aby na wykresie dodać nową kolumnę "predykcje" do ramki danych zawierającej prognozy modelu. Jednak, że nie jest bardzo ogólny (wyobrazić nasz model jest nieliniowa, a więc chcemy naszych działek, aby pokazać gładkie krzywe), więc zamiast tego zrobić nowy dataframe ze wszystkich kombinacji czynników predykcyjnych, następnie wygenerować prognozy:

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

preds dataframe wygląda następująco:

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 

Od Seaborn działka poleceń (w przeciwieństwie ggplot2 poleceń w R) wydaje się, aby przyjąć jedną i tylko jeden dataframe, musimy połączyć nasze prognozy do surowych danych:

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

Możemy teraz wykreślić modelu prognozy wraz z danymi z naszej ciągłej zmiennej x1 jako osi 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

tej pory tak dobrze. Teraz wyobraźmy sobie, że nie mierzyliśmy zmiennej ciągłej x1 i wiemy tylko o dwóch pozostałych (kategorycznych) zmiennych (tj. Mamy strukturę czynnikową 2x2). W jaki sposób możemy wykreślić przewidywania modelu względem danych w tym przypadku?

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) 

Cóż, możemy wykreślić modeli przewidywania użyciu sns.pointplot lub podobny, jak w przykładzie:

# 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

lub dane za pomocą sns.factorplot tak:

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

enter image description here

Ale nie widzę sposobu tworzenia wykresu podobnego do pierwszego (np. linie dla przewidywań modelu za pomocą plt.plot, punkt rozproszenia danych za pomocą plt.scatter). Powodem jest to, że zmienna x2, którą próbuję użyć jako osi X, jest ciągiem/obiektem, więc komendy pyplot nie wiedzą, co z nimi zrobić.

+0

Zauważ, że rozpoznaję, że linie na ostatnim wykresie są takie same jak linie na drugim wykresie (tj. prognozy modelu są liniami między środkami). To nie zawsze będzie prawdą, więc popieram bardziej ogólne podejście. – tsawallis

+0

Należy również zauważyć, że z jakiegoś nieznanego powodu legenda na drugim wykresie nie pokazuje przypadków "c" i "d", a jedynie tytuł legendy. Nie wiem dlaczego. – tsawallis

+0

Możesz przekazać dowolną funkcję do 'FacetGrid.map', dopóki pobiera argumenty pozycyjne i działki' x', 'y' na" aktualnie "aktywne osie. Więc powinieneś być w stanie zdefiniować funkcję, która mapuje z twoich kategorii do [0, 1, 2, ...] i używasz tego. To pomaga? – mwaskom

Odpowiedz

4

Jak już wspomniałem w moich komentarzach, myślę o tym na dwa sposoby.

Pierwszym jest zdefiniowanie funkcji, które wykonuje dopasowanie, a następnie działek i przekazać go do 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

Drugi to obliczyć przewidywane wartości, a następnie połączyć je w DataFrame z dodatkowej zmiennej, która określa to, co jest dane, a co model:

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

Powiązane problemy