Это мой DataFrame:
import pandas as pd
import numpy as np
df = pd.DataFrame(
{
'x': [1, np.nan, 3, np.nan, 5],
'y': [np.nan, 7, 8, 9, np.nan],
'x_a': [1, 2, 3, 4, 5],
'y_a': [6, 7, 8, 9, 10]
}
)
Ожидаемый результат: fill_na столбцы x и y:
x y x_a y_a
0 1.0 6.0 1 6
1 2.0 7.0 2 7
2 3.0 8.0 3 8
3 4.0 9.0 4 9
4 5.0 10.0 5 10
По сути, я хочу заполнить xx_a и yy_a. Другими словами, каждый столбец должен быть связан с другим столбцом, имеющим суффикс _a и имя столбца.
Я могу получить этот вывод, используя этот код:
for col in ['x', 'y']:
df[col] = df[col].fillna(df[f'{col}_a'])
Но мне интересно, является ли это лучшим/наиболее эффективным способом? Предположим, у меня есть сотни таких столбцов.
@Ben.T спасибо за ответ. Очевидно, что если решение связано с проверкой, оно является более общим. Но это нормально в любом случае.
Будет ли у вас список cols со всеми столбцами? Для большого набора данных.
@ U13-Вперед, спасибо за ответ. Я могу получить список всех столбцов с помощью df.columns. Можно подробнее, если что-то не понятно?
@AmirX Я понял, все в порядке, ты можешь проверить ответы и узнать, помогут ли они.






А как насчет использования индекса для выбора всех столбцов одновременно и set_axis для выравнивания DataFrame:
cols = pd.Index(['x', 'y'])
df[cols] = df[cols].fillna(df[cols+'_a'].set_axis(cols, axis=1))
Примечание. это предполагает, что все столбцы в cols и все столбцы «_a» существуют. Если вы не уверены, что можете быть в безопасности и можете использовать пересечение и переиндексацию:
cols = pd.Index(['x', 'y']).intersection(df.columns)
df[cols] = df[cols].fillna(df.reindex(columns=cols+'_a').set_axis(cols, axis=1))
Или для подхода, который полностью независим от явной передачи входных столбцов и просто полагается на суффикс (_a):
suffix = '_a'
# find columns "xyz" that have a "xyz_a" counterpart
c1 = df.columns.intersection(df.columns+suffix)
c2 = c1.str.removesuffix(suffix)
# select, fillna, update
df[c2] = df[c2].fillna(df[c1].set_axis(c2, axis=1))
Выход:
x y x_a y_a
0 1.0 6.0 1 6
1 2.0 7.0 2 7
2 3.0 8.0 3 8
3 4.0 9.0 4 9
4 5.0 10.0 5 10
Пример, для которого потребуется второй подход:
df = pd.DataFrame(
{
'x': [1, np.nan, 3, np.nan, 5],
'z': [np.nan, 7, 8, 9, np.nan],
'p_a': [1, 2, 3, 4, 5],
'y_a': [6, 7, 8, 9, 10]
}
)
Вы можете использовать фильтр , чтобы получить все столбцы с _a, создать список столбцов без _a. затем fillna с диктом.
cols_a = df.filter(like='_a').columns
cols = [_c.strip('_a') for _c in cols_a]
df = df.fillna({_c:df[_c_a] for _c, _c_a in zip(cols, cols_a)})
print(df)
# x y x_a y_a
# 0 1.0 6.0 1 6
# 1 2.0 7.0 2 7
# 2 3.0 8.0 3 8
# 3 4.0 9.0 4 9
# 4 5.0 10.0 5 10
Вы можете использовать df.combine_first:
cols = ['x', 'y']
df[cols] = df[cols].combine_first(
df.filter(regex='_a$').rename(lambda x: x.rstrip('_a'), axis=1)
)
Выход:
x y x_a y_a
0 1.0 6.0 1 6
1 2.0 7.0 2 7
2 3.0 8.0 3 8
3 4.0 9.0 4 9
4 5.0 10.0 5 10
Если вы ожидаете дополнительных суффикс-столбцов (например, z_a), добавьте [cols] на всякий случай:
df[cols] = df[cols].combine_first(
df.filter(regex='_a$').rename(lambda x: x.rstrip('_a'), axis=1)
)[cols]
Подход также должен работать, если один из ваших столбцов не соответствует варианту суффикса. То есть:
df = pd.DataFrame(
{
'x': [1, np.nan, 3, np.nan, 5],
'y': [np.nan, 7, 8, 9, np.nan],
'x_a': [1, 2, 3, 4, 5],
# 'y_a': [6, 7, 8, 9, 10],
'z_a': [11, 12, 13, 14, 15]
}
)
Выход:
x y x_a z_a
0 1.0 NaN 1 11
1 2.0 7.0 2 12
2 3.0 8.0 3 13
3 4.0 9.0 4 14
4 5.0 NaN 5 15
Обновлено: сценарий без предустановленного списка столбцов (cols). В этом случае вы можете начать со столбцов суффиксов:
df = (df
.filter(regex='_a$')
.rename(lambda x: x.rstrip('_a'), axis=1)
.combine_first(df)[df.columns]
)
Это хорошо (+1), но это может потерпеть неудачу, если у вас есть дополнительные столбцы _a. Я бы посоветовал reindex(columns=cols) после combine_first, если вы хотите быть в безопасности ;)
@mozway: хорошая мысль. Я полагаю, добавление [cols] должно помочь в этом сценарии, не так ли? Типа: df[cols].combine_first(other)[cols], или это будет как-то дорого?
Честно говоря, я не знаю, быстрее ли нарезка или reindex. Если нарезать, то первое можно опустить [cols] ;)
@mozway: да, я это понял (что могу опустить первый [cols]), но задаюсь аналогичным вопросом: df.combine_first(other)[cols] против df[cols].combine_first(other)[cols] с точки зрения производительности;)? Посмотрим, смогу ли я найти время позже, чтобы проверить эти вещи. еще раз спасибо
Если у вас много дополнительных столбцов, df[cols].combine_first(other)[cols] будет использовать меньше промежуточной памяти.
Вы уверены, что в ОП есть список со всеми столбцами?'
Более эффективный подход:
a_cols = pd.Series(cols).add('_a')
df[cols] = df[cols].fillna(df.reindex(a_cols, axis=1).set_axis(cols, axis=1).dropna(how='all', axis=1))
Короче, но менее эффективно:
df.apply(lambda x: x.fillna(value=df.get(x.name[0] + '_a', x)))
Выход:
x y x_a y_a
0 1.0 6.0 1 6
1 2.0 7.0 2 7
2 3.0 8.0 3 8
3 4.0 9.0 4 9
4 5.0 10.0 5 10
Тестирование с использованием dataframe с разными значениями:
df = pd.DataFrame(
{
'x': [1, np.nan, 3, np.nan, 5],
'y': [np.nan, 7, 8, 9, np.nan],
'x_a': [1, 2, 3, 4, 5],
# 'y_a': [6, 7, 8, 9, 10],
'z_a': [11, 12, 13, 14, 15]
}
)
Выход:
x y x_a z_a
0 1.0 NaN 1 11
1 2.0 7.0 2 12
2 3.0 8.0 3 13
3 4.0 9.0 4 14
4 5.0 NaN 5 15
Это даже хуже оригинального подхода, не так ли? Вы не только перебираете все столбцы, но даже обрабатываете те, у которых нет аналога, заполняя их самими собой.
@mozway Проверьте мои изменения, кстати, спасибо за ваш вклад!
@mozway снова отредактировано
Это, наверное, лучше, но почти идентично моему сейчас подходу;)
@mozway Ах, я только что посмотрел, да, я не видел твоего подхода раньше, в любом случае, честная игра, чувак. +1 для тебя.
Другое возможное решение, использующее np.where:
df.loc[:, 'x':'y'] = np.where(
df.loc[:, 'x':'y'].isna(), df.loc[:, 'x_a':'y_a'], df.loc[:, 'x':'y'])
Используя df.loc[:, 'x':'y'] и df.loc[:, 'x_a':'y_a'], мы также могли бы использовать fillna. Однако столбцы в каждом из двух блоков должны быть смежными.
Выход:
x y x_a y_a
0 1.0 6.0 1 6
1 2.0 7.0 2 7
2 3.0 8.0 3 8
3 4.0 9.0 4 9
4 5.0 10.0 5 10
import pandas as pd
import numpy as np
df = pd.DataFrame(
{
'x': [1, np.nan, 3, np.nan, 5],
'y': [np.nan, 7, 8, 9, np.nan],
'x_a': [1, 2, 3, 4, 5],
'y_a': [6, 7, 8, 9, 10]
}
)
# Identify the columns to fill and their corresponding '_a' columns
suffix = '_a'
cols = pd.Index([col for col in df.columns if not col.endswith(suffix)])
print(cols)
#cols_a = cols + suffix
#print(cols_a)
cols_a = np.array([col for col in df.columns if col.endswith(suffix)])
# Fill NaN values in the original columns with values from the '_a' columns
df[cols] = df[cols].combine_first(df[cols_a].set_axis(cols,axis=1))
print(df)
'''
x y x_a y_a
0 1.0 6.0 1 6
1 2.0 7.0 2 7
2 3.0 8.0 3 8
3 4.0 9.0 4 9
4 5.0 10.0 5 10
'''
df.drop(columns = cols_a, inplace = True)
print(df)
'''
x y
0 1.0 6.0
1 2.0 7.0
2 3.0 8.0
3 4.0 9.0
4 5.0 10.0
'''
все столбцы без суффикса _a должны быть заполнены, и у вас наверняка есть соответствующий столбец, или вам нужно реализовать некоторую проверку, чтобы убедиться, что все пары существуют?