Pandas: правильный способ установки значений на основе условия для подмножества мультииндексного фрейма данных

Я не уверен, как это сделать без цепных назначений (что, вероятно, все равно не сработает, потому что я буду устанавливать копию).

Я не хочу брать подмножество мультииндексного кадра данных pandas, проверять значения меньше нуля и устанавливать их равными нулю.

Например:

df = pd.DataFrame({('A','a'): [-1,-1,0,10,12],
                   ('A','b'): [0,1,2,3,-1],
                   ('B','a'): [-20,-10,0,10,20],
                   ('B','b'): [-200,-100,0,100,200]})

df[df['A']<0] = 0.0

дает

In [37]:

df

Out[37]:
    A   B
    a   b   a   b
0   -1  0   -20 -200
1   -1  1   -10 -100
2   0   2   0   0
3   10  3   10  100
4   12  -1  20  200

Что показывает, что он не смог установить на основе условия. В качестве альтернативы, если бы я выполнил цепное задание:

df.loc[:,'A'][df['A']<0] = 0.0

Это дает тот же результат (и настройку с предупреждением о копировании)

Я мог бы перебрать каждый столбец, исходя из условия, что первый уровень — это тот, который мне нужен:

for one,two in df.columns.values:
    if one == 'A':
        df.loc[df[(one,two)]<0, (one,two)] = 0.0

что дает желаемый результат:

In [64]:

df

Out[64]:
    A   B
    a   b   a   b
0   0   0   -20 -200
1   0   1   -10 -100
2   0   2   0   0
3   10  3   10  100
4   12  0   20  200

Но почему-то я чувствую, что есть лучший способ сделать это, чем перебирать столбцы. Каков наилучший способ сделать это в пандах?


person pbreach    schedule 17.01.2015    source источник


Ответы (1)


Это приложение (и одна из основных причин использования слайсеров MultiIndex), см. документы здесь

In [20]: df = pd.DataFrame({('A','a'): [-1,-1,0,10,12],
                   ('A','b'): [0,1,2,3,-1],
                   ('B','a'): [-20,-10,0,10,20],
                   ('B','b'): [-200,-100,0,100,200]})

In [21]: df
Out[21]: 
    A      B     
    a  b   a    b
0  -1  0 -20 -200
1  -1  1 -10 -100
2   0  2   0    0
3  10  3  10  100
4  12 -1  20  200

In [22]: idx = pd.IndexSlice

In [23]: mask = df.loc[:,idx['A',:]]<0

In [24]: mask
Out[24]: 
       A       
       a      b
0   True  False
1   True  False
2  False  False
3  False  False
4  False   True

In [25]: df[mask] = 0

In [26]: df
Out[26]: 
    A      B     
    a  b   a    b
0   0  0 -20 -200
1   0  1 -10 -100
2   0  2   0    0
3  10  3  10  100
4  12  0  20  200

Поскольку вы работаете с 1-м уровнем индекса столбцов, следующее также будет работать. Приведенный выше пример является более общим, скажем, вы хотели сделать это для «а».

In [30]: df[df[['A']]<0] = 0

In [31]: df
Out[31]: 
    A      B     
    a  b   a    b
0   0  0 -20 -200
1   0  1 -10 -100
2   0  2   0    0
3  10  3  10  100
4  12  0  20  200
person Jeff    schedule 17.01.2015
comment
А ладно спасибо! Использование слайсеров для создания маски выглядит действительно полезным (возможно, придется использовать это в большей части моего кода). Второй пример действительно решает мою конкретную проблему. Я не знал о разнице между df['A'] и df[['A']] - person pbreach; 17.01.2015