2015-03-12 2 views
4

Я хотел бы одновременно заменить значения нескольких столбцов соответствующими значениями в других столбцах на основе значений в первой группе столбцов (в частности, где один из первые столбцы пустые). Вот пример того, что я пытаюсь сделать:Условная замена нескольких столбцов на основе значений столбцов в pandas DataFrame

import pandas as pd 

df = pd.DataFrame({'a1':['m', 'n', 'o', 'p'], 
        'a2':['q', 'r', 's', 't'], 
        'b1':['', '', 'a', '' ], 
        'b2':['', '', 'b', '']}) 

df 

# a1 a2 b1 b2 
# 0 m q 
# 1 n r 
# 2 o s a b 
# 3 p t 

Я хотел бы заменить «» значения b1 и b2 с соответствующими значениями a1 и a2, где b1 является пустым:

# a1 a2 b1 b2 
# 0 m q m q 
# 1 n r n r 
# 2 o s a b 
# 3 p t p t 

Вот мой мыслительный процесс (я относительно новым для панд, так что я, вероятно, говоря с сильным акцентом R здесь):

missing = (df.b1 == '') 

# First thought: 
df[missing, ['b1', 'b2']] = df[missing, ['a1', 'a2']] 
# TypeError: 'Series' objects are mutable, thus they cannot be hashed 

# Fair enough 
df[tuple(missing), ('b1', 'b2')] = df[tuple(missing), ('a1', 'a2')] 
# KeyError: ((True, True, False, True), ('a1', 'a2')) 

# Obviously I'm going about this wrong. Maybe I need to use indexing? 
df[['b1', 'b2']].ix[missing,:] 
# b1 b2 
# 0  
# 1  
# 3  

# That looks right 
df[['b1', 'b2']][missing, :] = df[['a1', 'a2']].ix[missing, :] 
# TypeError: 'Series' objects are mutable, thus they cannot be hashed 
# Deja vu 

df[['b1', 'b2']].ix[tuple(missing), :] = df[['a1', 'a2']].ix[tuple(missing), :] 
# ValueError: could not convert string to float: 
# Uhh... 

Я мог бы сделать это столбец за столбцом:

df['b1'].ix[missing] = df['a1'].ix[missing] 
df['b2'].ix[missing] = df['a2'].ix[missing] 

... но я подозреваю, что существует более идиоматический способ сделать это. Мысли?

Обновление: Чтобы уточнить, я задаюсь вопросом, можно ли одновременно обновлять все столбцы. Например, гипотетический модификация ответа Primer (это не работает, и результаты в NaNs, хотя я не уверен, почему):

df.loc[missing, ['b1', 'b2']] = f.loc[missing, ['a1', 'a2']] 

# a1 a2 b1 b2 
# 0 m q NaN NaN 
# 1 n r NaN NaN 
# 2 o s a b 
# 3 p t NaN NaN 

ответ

5

Как насчет

df[['b1', 'b2']] = df[['b1', 'b2']].where(df[['b1', 'b2']] != '', df[['a1', 'a2']].values) 

это возвращает

a1 a2 b1 b2 
0 m q m q 
1 n r n r 
2 o s a b 
3 p t p t 
1

Вы могли бы сделать это следующим образом:

mask1 = df.b1.str.len() == 0 
mask2 = df.b2.str.len() == 0 
df.loc[mask1, 'b1'] = df.loc[mask1, 'a1'] 
df.loc[mask2, 'b2'] = df.loc[mask2, 'a2'] 
print df 

    a1 a2 b1 b2 
0 m q m q 
1 n r n r 
2 o s a b 
3 p t p t 

Или имеющие маски, как это будет работать:

mask1 = df.b1 == '' 
mask2 = df.b2 == '' 
+0

Этот метод является на самом деле медленнее, чем ОП, это было так же, как мой ответ, который я удалил, потому что он медленнее – EdChum

+0

Спасибо за ответ. Я надеялся сделать это за один шаг для всех столбцов, вместо того, чтобы делать это по столбцу (все еще не уверен, что это возможно). Например: 'df.loc [missing, ['b1', 'b2']] = df.loc [missing, ['a1', 'a2']] ' – danpelota

+0

Мой плохой, как-то неправильно понял вопрос ... Во всяком случае, Алекс предложил другой подход с' .where', который является хорошим однострочным словом, который вы ищете. Вы также можете использовать '.ix' вместо' .loc', который иногда дает несколько лучшие результаты с точки зрения скорости (если это проблема). – Primer

1

Как насчет:

missing = df.loc[:] == "" 
shifted = df.copy().shift(2, axis=1) 
df[missing] = shifted 

Другими словами, построить missing булеву маску ячеек, где отсутствуют данные, а также копию исходных данных со всеми столбцами сдвинуты два места справа. Затем присвойте сдвинутые данные исходным данным, но только там, где они отсутствовали в первую очередь.

Эти данные будут протекать так:

data progression

только клетки, отмеченные зеленым цветом в missing будут скопированы.

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

df[df.loc[:] == ""] = df.copy().shift(2, axis=1) 
Смежные вопросы