Условный оператор для создания многоколоночных значений в Python

#python #pandas #dataframe

Вопрос:

Я пытаюсь заменить значения в столбцах «Alloc1» и «Alloc2» на основе условия в одном столбце «Число» в приведенном ниже фрейме данных.

 data = {'ID': ['001', '002', '003', '004'], 'Number': [99, 99, 20, 40], 'Alloc1': [np.NaN, np.NaN, np.NaN, np.NaN], 'Alloc2': [np.NaN, np.NaN, np.NaN, np.NaN]}
# Create DataFrame.
df = pd.DataFrame(data)
 

Мой код для вставки значений на основе условия приведен ниже:-

 for  numbers  in df["Number"]:
    
    if  (numbers == 99):
        df["Alloc1"] = 31
        df["Alloc2"] = 3

    else:
        df["Alloc1"] = 0
        df["Alloc2"] = numbers/2 
 

Вышесказанное, по-видимому, выполняет только другую часть инструкции и для последнего значения в столбце «Число», которое не равно 99. Как я могу это исправить? Функция будет отличной. Идеальный результат должен быть:-

 final = {'ID': ['001', '002', '003', '004'], 'Number': [99, 99, 20, 40], 'Alloc1': [31, 31, 0, 0], 'Alloc2': [3, 3, 10, 20]}
# Create DataFrame.
final_df = pd.DataFrame(final)

 

Комментарии:

1. В вашем ожидаемом выводе должны Alloc2 быть значения 3, 3, 10, 20 вместо 3, 3, 2, 2 ? Логика подразумевает, что если Number не 99, установите Alloc2 = Number / 2

2. df["Alloc1"] = 31 присваивает константе весь столбец . Вам нужно только присвоить значение в определенной строке (той же строке, из которой взят номер) новым значениям. Однако лучшим подходом было бы что-то вроде np.where .

3. Спасибо @PeterLeimbigler за исправление. Отредактировал его.

Ответ №1:

Подумайте, что «векторизованное» решение будет иметь лучшую производительность, чем это, и либо это, либо where версия более «хороши в стиле панд». Этот ответ просто показывает вам, как достичь того, чего вы хотели, используя больше похожий подход, которому вы следовали. Это не очень «панд» способ делать что-то, но может быть полезно для понимания того, почему то, что вы пытались, не сработало.

 import pandas as pd

data = {'ID': ['001', '002', '003', '004'],
        'Number': [99, 99, 20, 40]}
        # Don't actually need the NaN-filled 'Alloc1' and 'Alloc2' yet
        # Those columns get created when you give them values, later
df = pd.DataFrame(data)

def allocateCodes(row):
    if (row['Number'] == 99):
        row['Alloc1'] = 31
        row['Alloc2'] = 3
    else:
        row['Alloc1'] = 0
        row['Alloc2'] = row['Number'] / 2
    return row

# axis="columns" means go 'take each row' (i.e., a whole set of columns)
# at a time (can also use axis=1)
# instead of 'take each column' (axis="rows" / axis=0)      
outputDf = df.apply(allocateCodes, axis="columns")

print(outputDf)
 

Выходы:

     ID  Number  Alloc1  Alloc2
0  001      99      31     3.0
1  002      99      31     3.0
2  003      20       0    10.0
3  004      40       0    20.0
 

Комментарии:

1. Большое вам спасибо за такой подход. На самом деле это отражает мой мыслительный процесс.

Ответ №2:

Предполагая , что вы можете безопасно перезаписать все столбцы Alloc1 и Alloc2 , вы можете использовать np.where , как предложил Генри Экер:

 df['Alloc1'] = np.where(df['Number'] == 99, 31, 0)
df['Alloc2'] = np.where(df['Number'] == 99, 3, df['Number'] / 2).astype(int)

print(df)
    ID  Number  Alloc1  Alloc2
0  001      99      31       3
1  002      99      31       3
2  003      20       0      10
3  004      40       0      20
 

Комментарии:

1. Это намного элегантнее, чем у меня, потрясающе.

2. Спасибо тебе за решение, Питер. Тоже хорошо работает.

3. Спасибо @BaronLegendre. Ваше решение имеет то преимущество, что читателю не нужно думать о том, как np.where оно работает 🙂

Ответ №3:

Попробуйте использовать векторизованную операцию для решения этой проблемы

 import pandas as pd

data = {'ID': ['001', '002', '003', '004'], 'Number': [99, 99, 20, 40], 'Alloc1': [np.NaN, np.NaN, np.NaN, np.NaN], 'Alloc2': [np.NaN, np.NaN, np.NaN, np.NaN]}
# Create DataFrame.
df = pd.DataFrame(data)

df['Alloc1'] = 0
df['Alloc2'] = df['Number']/2
df.loc[df['Number'] == 99,'Alloc1'] = 31
df.loc[df['Number'] == 99,'Alloc2'] = 3
df
 
 output
    ID  Number  Alloc1  Alloc2
0  001      99      31     3.0
1  002      99      31     3.0
2  003      20       0    10.0
3  004      40       0    20.0
 

Комментарии:

1. Спасибо, барон, за решение. Тоже хорошо работает.