2015-12-30 11 views
5

Da un età colonna numerica panda, Discretizza come invecchiato con qCut, creiamo confini aperti dai limiti qCut:Crea Bounds indicatori aperti da panda get_dummies su discretizzato numerica

import pandas as pd 
from itertools import chain 

d = {'age': {0: 5, 1: 23, 2: 43, 3: 70, 4: 30}} 
df = pd.DataFrame.from_dict(d) 
df['ageD'] = pd.qcut(df.iloc[:, 0], 2) 
df.ageD.cat.categories 
# Index([u'[5, 30]', u'(30, 70]'], dtype='object') 

dall'indice ([u '[5, 30]', u '(30, 70]'], 'oggetto' DTYPE =) facciamo bopens:

>>> bopens = get_open_bounds(df) 
>>> bopens 
# ['(-inf, 5]', '(-inf, 30]', '(-inf, 70]', '(5, +inf)', '(30, +inf)', '(70, +inf)'] 

Poi ci convertono in variabile categoriale manichino/indicatore variabili con get_dummies:

df = pd.get_dummies(df) 
print df 
# age ageD_[5, 30] ageD_(30, 70] 
# 0 5    1    0 
# 1 23    1    0 
# 2 43    0    1 
# 3 70    0    1 
# 4 30    1    0 

voglio arricchire il frame di dati con i limiti colonne aperte, df.shape saranno abbastanza grande, ~ (10E6, 32). Qual è il modo migliore per fare per ogni riga i 6 bopen cols?

Il df obiettivo sarà simile a questo:

>>> df 
    age age_[5, 30] age_(30, 70] (-inf, 5] (-inf, 30] (-inf, 70] (5, +inf) (30, +inf) (70, +inf) 
0 5   1    0   1   1   1   0   0   0 
1 23   1    0   0   1   1   1   0   0 
2 43   0    1   0   0   1   1   1   0 
3 70   0    1   0   0   1   1   1   0 
4 30   1    0   0   1   1   1   0   0 

PS: i get_open_bounds usati per fare bopens:

def get_open_bounds(df): 
    bounds = [(int(x[1:]), int(y[:-1])) for x, y in 
      [c.split(', ') for c in df.ageD.cat.categories]] 
    bounds = list(chain(*bounds)) 
    bounds 
    # [5, 30, 30, 70] 

    # to get uniques, keeping the order 
    bounds = [b for idx, b in enumerate(bounds) if b not in bounds[:idx]] 

    # make the open bounds 
    bopens = ["(-inf, {}]".format(b) for b in bounds] + \ 
      ["({}, +inf)".format(b) for b in bounds] 
    return bopens 

risposta

2

IIUC, si può fare questo con un po 'di trasmissione:

df['ageD'], bins = pd.qcut(df.iloc[:, 0], 2, retbins=True) 
left = (df["age"].values <= bins[:,None]).T.astype(int) 
dl = pd.DataFrame(left, columns=["(-inf, {}]".format(b) for b in bins]) 
dr = pd.DataFrame(1-left, columns=["({}, +inf)".format(b) for b in bins]) 
dout = pd.concat([pd.get_dummies(df), dl, dr], axis=1) 

mi dà

>>> dout 
    age ageD_[5, 30] ageD_(30, 70] (-inf, 5] (-inf, 30] (-inf, 70] (5, +inf) (30, +inf) (70, +inf) 
0 5    1    0   1   1   1   0   0   0 
1 23    1    0   0   1   1   1   0   0 
2 43    0    1   0   0   1   1   1   0 
3 70    0    1   0   0   1   1   1   0 
4 30    1    0   0   1   1   1   0   0 

Nota # 1: con l'aggiunta di retbins = True posso ottenere i bidoni stessi e evitare alcuni imbarazzante parsing della stringa.

Nota n. 2: eseguendo un "right = 1 - left" implicito, suppongo che nessun età sia NaN e quindi uno di> = o < deve essere true; se non è sicuro, puoi fare right = (df["age"].values > bins[:,None].T.astype(int).)

Nota n. 3: in realtà dovrei passare anche i costruttori di fotogrammi df.index - mentre il tuo esempio aveva l'indice canonico, potrebbe non essere vero nel tuo dati reali.

+0

cosa significa passare il df.index al costruttore del frame? – user3313834

+0

@ user3313834: DataFrame accetta diversi argomenti, tra cui 'data' (che cosa verrà riempito con),' columns' (i nomi delle colonne) e 'index'. Non ho passato 'index', quindi' dl' e 'dr' ottengono indici 0,1,2,3 .. Se il tuo' df' non ha 0,1,2,3 .. ecc, allora il la concatenazione darà risultati inaspettati perché gli indici non corrisponderanno. – DSM