2017-10-05 4 views
1

J'ai une trame de données comme de multiindice ceci:Comment soustraire des colonnes dans une base de données multi-index?

import pandas as pd 
import numpy as np 

df = pd.DataFrame({'ind1': list('aaaaaaaaabbbbbbbbb'), 
        'ind2': list('cccdddeeecccdddeee'), 
        'ind3': list(range(3))*6, 
        'val1': list(range(100, 118)), 
        'val2': list(range(70, 88))}) 

df_mult = df.set_index(['ind1', 'ind2', 'ind3']) 

       val1 val2 
ind1 ind2 ind3    
a c 0  100 70 
      1  101 71 
      2  102 72 
    d 0  103 73 
      1  104 74 
      2  105 75 
    e 0  106 76 
      1  107 77 
      2  108 78 
b c 0  109 79 
      1  110 80 
      2  111 81 
    d 0  112 82 
      1  113 83 
      2  114 84 
    e 0  115 85 
      1  116 86 
      2  117 87 

Ce que je veux faire est de soustraire les valeurs df_mult.loc['a', 'e', :] et df_mult.loc['b', 'e', :] respectivement des valeurs correspondant à df_mult.loc['a', ['c', 'd'], :] et df_mult.loc['b', ['c', 'd'], :], respectivement. Le résultat attendu serait

   val1 val2 
ind1 ind2 ind3    
a c 0  -6 -6 
      1  -6 -6 
      2  -6 -6 
    d 0  -3 -5 
      1  -3 -5 
      2  -3 -5 
    e 0  106 76 
      1  107 77 
      2  108 78 
b c 0  -6 -6 
      1  -6 -6 
      2  -6 -6 
    d 0  -3 -3 
      1  -3 -3 
      2  -3 -3 
    e 0  115 85 
      1  116 86 
      2  117 87 

Idéalement, quelque chose comme ça marcherait

df_mult.loc['a', ['c', 'd'], :].subtract(df_mult.loc['a', 'e', :]) 

mais cela me donne beaucoup de NaNs.

Comment est-ce que je ferais ceci?

+0

Est-ce-Python 2.x? –

+0

@WillemVanOnsem: Oui, c'est le cas. – Cleb

Répondre

3

MAJ2: avec kind help of @Divakar:

def repeat_blocks(a, repeats=2, block_length=None): 
    N = a.shape[0] 
    if not block_length: 
     block_length = N//2 
    out = np.repeat(a.reshape(N//block_length,block_length,-1), 
        repeats, 
        axis=0) \ 
      .reshape(N*repeats,-1) 
    return out 

In [234]: df_mult.loc[idx[['a','b'], ['c', 'd'], :], :] -= repeat_blocks(df_mult.loc[['a','b'], 'e', :].values) 

In [235]: df_mult 
Out[235]: 
       val1 val2 
ind1 ind2 ind3 
a c 0  -6 -6 
      1  -6 -6 
      2  -6 -6 
    d 0  -3 -3 
      1  -3 -3 
      2  -3 -3 
    e 0  106 76 
      1  107 77 
      2  108 78 
b c 0  -6 -6 
      1  -6 -6 
      2  -6 -6 
    d 0  -3 -3 
      1  -3 -3 
      2  -3 -3 
    e 0  115 85 
      1  116 86 
      2  117 87 

MISE À JOUR:

In [100]: idx = pd.IndexSlice 

In [102]: df_mult.loc[idx['a', ['c', 'd'], :], :] -= \ 
       np.concatenate([df_mult.loc['a', 'e', :].values] * 2) 

In [103]: df_mult 
Out[103]: 
       val1 val2 
ind1 ind2 ind3 
a c 0  -6 -6 
      1  -6 -6 
      2  -6 -6 
    d 0  -3 -3 
      1  -3 -3 
      2  -3 -3 
    e 0  106 76 
      1  107 77 
      2  108 78 
b c 0  109 79 
      1  110 80 
      2  111 81 
    d 0  112 82 
      1  113 83 
      2  114 84 
    e 0  115 85 
      1  116 86 
      2  117 87 

Vieux (incorrect) réponse:

In [62]: df_mult.loc['a', 'e', :] -= df_mult.loc['b', 'e', :].values 

In [63]: df_mult 
Out[63]: 
       val1 val2 
ind1 ind2 ind3 
a c 0  100 70 
      1  101 71 
      2  102 72 
    d 0  103 73 
      1  104 74 
      2  105 75 
    e 0  -9 -9 
      1  -9 -9 
      2  -9 -9 
b c 0  109 79 
      1  110 80 
      2  111 81 
    d 0  112 82 
      1  113 83 
      2  114 84 
    e 0  115 85 
      1  116 86 
      2  117 87 
+0

Merci pour la réponse rapide; Je pourrais manquer quelque chose, mais cela ne ressemble pas au résultat souhaité. – Cleb

+0

Je ne comprends pas très bien ce que vous voulez soustraire de ce que ... – MaxU

+0

Désolé d'être pas assez précis. Pour chaque valeur dans 'ind1', je veux soustraire toutes les valeurs correspondant à' e' (dans 'ind2') de toutes les valeurs restantes dans' ind2'. Par exemple. '(a, c, 0) = 100' -' (a, e, 0) = 106' serait '-6' (voir mon résultat désiré dans la question). Pareil pour '(a, c, 1) = 101' -' (a, e, 1) = 107' est aussi '-6' et ainsi de suite. Est-ce que cela le rend plus clair? – Cleb

2

Vous cherchez quelque chose comme? (df ici égale df_mult)

idx = pd.IndexSlice 
df.loc[idx['a', ['c', 'd'], :],idx['val1','val2']]=df.loc['a', ['c', 'd'], :].values-np.tile(df.loc['a', 'e', :].values, (2, 1)) 

df 
Out[608]: 
       val1 val2 
ind1 ind2 ind3    
a c 0  -6 -6 
      1  -6 -6 
      2  -6 -6 
    d 0  -3 -3 
      1  -3 -3 
      2  -3 -3 
    e 0  106 76 
      1  107 77 
      2  108 78 
b c 0  109 79 
      1  110 80 
      2  111 81 
    d 0  112 82 
      1  113 83 
      2  114 84 
    e 0  115 85 
      1  116 86 
      2  117 87 
+0

oui, nous l'avons posté en même temps ;-) +1 – MaxU

+1

@MaxU merci mec, j'ai déjà voté pour vous, quand je vous ai vu répondre, je vous montre la clé pour résoudre le problème. – Wen

+0

Cela semble fonctionner, merci (upvoted)! Existe-t-il un moyen facile de faire de même pour 'b' dans le même tour pour' a' ou devrais-je faire une boucle sur tous les éléments dans 'ind1'? Aussi, est-il possible d'éviter 'idx ['val1', 'val2']' car j'ai beaucoup de colonnes? – Cleb