#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. Спасибо, барон, за решение. Тоже хорошо работает.