2013-12-11 19 views
10

Creazione mia dataframe:valori di indice ridenominazione in dataframe multiindex

from pandas import * 
arrays = [['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'], 
      ['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two']] 

tuples = zip(*arrays) 

index = MultiIndex.from_tuples(tuples, names=['first','second']) 
data = DataFrame(randn(8,2),index=index,columns=['c1','c2']) 

data 
Out[68]: 
        c1  c2 
first second      
bar one  0.833816 -1.529639 
     two  0.340150 -1.818052 
baz one -1.605051 -0.917619 
     two -0.021386 -0.222951 
foo one  0.143949 -0.406376 
     two  1.208358 -2.469746 
qux one -0.345265 -0.505282 
     two  0.158928 1.088826 

vorrei rinominare i "primi" valori di indice, come ad esempio "bar" -> "gatto", "baz" -> "cane ", ecc. Tuttavia, ogni esempio che ho letto funziona su un indice a livello singolo e/o su cicli attraverso l'intero indice per ricrearlo efficacemente da zero. Stavo pensando qualcosa come:

data = data.reindex(index={'bar':'cat','baz':'dog'}) 

ma questo non funziona, né mi aspetto davvero che funzioni su più indici. Posso fare una tale sostituzione senza dover scorrere l'intero indice del dataframe?

Inizia modifica

io sono riluttanti ad aggiornare a 0,13 fino al rilascio, quindi ho usato la seguente soluzione:

index = data.index.tolist() 
for r in xrange(len(index)): 
    index[r] = (codes[index[r][0]],index[r][1]) 

index = pd.MultiIndex.from_tuples(index,names=data.index.names) 
data.index = index 

Dove si trova un precedente dizionario definito di codice: coppie di stringhe. Questo in realtà non è il risultato più grande di quello che mi aspettavo (impiega un paio di secondi per operare su ~ 1,1 milioni di righe). Non è bello come un one-liner, ma funziona.

End Modifica

+1

Questa è attualmente una proposta di miglioramento per una versione futura di panda: https://github.com/pydata/pandas/issues/4160 (@unutbu soln funziona ATM però) – Jeff

risposta

16

utilizzare il metodo (new in version 0.13.0) set_levels:

data.index.set_levels([[u'cat', u'dog', u'foo', u'qux'], 
         [u'one', u'two']], inplace=True) 

rendimenti

    c1  c2 
first second      
cat one -0.289649 -0.870716 
     two -0.062014 -0.410274 
dog one  0.030171 -1.091150 
     two  0.505408 1.531108 
foo one  1.375653 -1.377876 
     two -1.478615 1.351428 
qux one  1.075802 0.532416 
     two  0.865931 -0.765292 

rimappare un livello basato su un dizionario, si potrebbe utilizzare una funzione come questa:

def map_level(df, dct, level=0): 
    index = df.index 
    index.set_levels([[dct.get(item, item) for item in names] if i==level else names 
         for i, names in enumerate(index.levels)], inplace=True) 

dct = {'bar':'cat', 'baz':'dog'} 
map_level(data, dct, level=0) 

Ecco un esempio eseguibile:

import numpy as np 
import pandas as pd 

arrays = [['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'], 
      ['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two']] 
tuples = zip(*arrays) 
index = pd.MultiIndex.from_tuples(tuples, names=['first','second']) 
data = pd.DataFrame(np.random.randn(8,2),index=index,columns=['c1','c2']) 
data2 = data.copy() 

data.index.set_levels([[u'cat', u'dog', u'foo', u'qux'], 
         [u'one', u'two']], inplace=True) 
print(data) 
#      c1  c2 
# first second      
# cat one  0.939040 -0.748100 
#  two -0.497006 -1.185966 
# dog one -0.368161 0.050339 
#  two -2.356879 -0.291206 
# foo one -0.556261 0.474297 
#  two  0.647973 0.755983 
# qux one -0.017722 1.364244 
#  two  1.007303 0.004337 

def map_level(df, dct, level=0): 
    index = df.index 
    index.set_levels([[dct.get(item, item) for item in names] if i==level else names 
         for i, names in enumerate(index.levels)], inplace=True) 
dct = {'bar':'wolf', 'baz':'rabbit'} 
map_level(data2, dct, level=0) 
print(data2) 
#      c1  c2 
# first second      
# wolf one  0.939040 -0.748100 
#  two -0.497006 -1.185966 
# rabbit one -0.368161 0.050339 
#  two -2.356879 -0.291206 
# foo one -0.556261 0.474297 
#  two  0.647973 0.755983 
# qux one -0.017722 1.364244 
#  two  1.007303 0.004337 
+0

0.13 è ancora in sviluppo, sono ancora in sviluppo in esecuzione 0.12.0. C'è qualche indicazione riguardo la stabilità di 0.13x? Non vedo molta documentazione per .index.set_levels. Nell'esempio sopra, l'impostazione dei livelli è semplice poiché abbiamo solo due livelli. È possibile passare un dizionario per sostituire solo i valori in un indice senza toccare (o dover specificare i valori per) gli altri assi? – tnknepp

+1

non funziona più a 0.16 –

+1

Funziona bene per me in 0.16.2 e 0.18.1 – joris

1

Il metodo set_levels stava causando i miei nuovi nomi di colonna per essere fuori uso. Così ho trovato una soluzione diversa che non è molto pulita, ma funziona bene. Il metodo è print df.index (o equivalentemente df.columns) e quindi copia e incolla l'output con i valori desiderati modificati. Ad esempio:

print data.index 

MultiIndex (livelli = [[ 'bar', 'baz', 'foo', 'qux'], [ 'uno', 'due']], etichette = [[ 0, 0, 1, 1, 2, 3, 3], [0, 1, 0, 1, 0, 1, 0, 1]], nomi = ['prima', 'seconda'])

data.index = MultiIndex(levels=[['new_bar', 'new_baz', 'new_foo', 'new_qux'], 
           ['new_one', 'new_two']], 
         labels=[[0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 0, 1, 0, 1, 0, 1]], 
         names=['first', 'second']) 

possiamo avere il pieno controllo su nomi modificando le etichette pure.Per esempio:

data.index = MultiIndex(levels=[['bar', 'baz', 'foo', 'qux'], 
           ['one', 'twooo', 'three', 'four', 
           'five', 'siz', 'seven', 'eit']], 
         labels=[[0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 2, 3, 4, 5, 6, 7]], 
         names=['first', 'second']) 

Si noti che in questo esempio abbiamo già fatto qualcosa di simile from pandas import MultiIndex o from pandas import *.

+0

Ho lo stesso problema con set_levels che mette i nuovi nomi di colonna fuori ordine. Penso che stia mettendo i nuovi nomi delle colonne sulla base del precedente parametro "labels" di MultiIndex. Bella soluzione. – Eddy

Problemi correlati