Объединить значения из 2 столбцов в один столбец в фрейме pandas
Я ищу метод, который ведет себя аналогично объединению в T-SQL. У меня есть 2 столбца (столбцы A и B), которые редко заполняются в фрейме pandas. Я хотел бы создать новый столбец, используя следующие правила:
- Если значение в столбце A не равно null, используйте это значение для нового столбца C
- Если значение в столбце A равно null, используйте значение в столбце B для нового столбца C
Как я уже упоминал, это можно выполнить в MS SQL Server с помощью функции coalesce. Я не нашел для этого хорошего питонического метода; существует ли?
Ответы
Ответ 1
используйте comb_first():
In [16]: df = pd.DataFrame(np.random.randint(0, 10, size=(10, 2)), columns=list('ab'))
In [17]: df.loc[::2, 'a'] = np.nan
In [18]: df
Out[18]:
a b
0 NaN 0
1 5.0 5
2 NaN 8
3 2.0 8
4 NaN 3
5 9.0 4
6 NaN 7
7 2.0 0
8 NaN 6
9 2.0 5
In [19]: df['c'] = df.a.combine_first(df.b)
In [20]: df
Out[20]:
a b c
0 NaN 0 0.0
1 5.0 5 5.0
2 NaN 8 8.0
3 2.0 8 2.0
4 NaN 3 3.0
5 9.0 4 9.0
6 NaN 7 7.0
7 2.0 0 2.0
8 NaN 6 6.0
9 2.0 5 2.0
Ответ 2
Попробуйте это также... проще запомнить:
df['c'] = np.where(df["a"].isnull(), df["b"], df["a"] )
Это немного быстрее: df['c'] = np.where(df["a"].isnull() == True, df["b"], df["a"] )
%timeit df['d'] = df.a.combine_first(df.b)
1000 loops, best of 3: 472 µs per loop
%timeit df['c'] = np.where(df["a"].isnull(), df["b"], df["a"] )
1000 loops, best of 3: 291 µs per loop
Ответ 3
combine_first
- самый простой вариант. Есть несколько других, которые я обрисую ниже. Я собираюсь изложить еще несколько решений, некоторые из которых применимы к различным случаям.
Случай № 1: не взаимоисключающие NaN
Не все строки имеют NaN, и они NaN
не являются взаимоисключающими между столбцами.
df = pd.DataFrame({
'a': [1.0, 2.0, 3.0, np.nan, 5.0, 7.0, np.nan],
'b': [5.0, 3.0, np.nan, 4.0, np.nan, 6.0, 7.0]})
df
a b
0 1.0 5.0
1 2.0 3.0
2 3.0 NaN
3 NaN 4.0
4 5.0 NaN
5 7.0 6.0
6 NaN 7.0
Пусть сначала объединятся на a
.
Series.mask
df['a'].mask(pd.isnull, df['b'])
# df['a'].mask(df['a'].isnull(), df['b'])
0 1.0
1 2.0
2 3.0
3 4.0
4 5.0
5 7.0
6 7.0
Name: a, dtype: float64
Series.where
df['a'].where(pd.notnull, df['b'])
0 1.0
1 2.0
2 3.0
3 4.0
4 5.0
5 7.0
6 7.0
Name: a, dtype: float64
Вы можете использовать похожий синтаксис, используя np.where
.
В качестве альтернативы, чтобы сначала объединить на b
, измените условия.
Случай № 2: взаимно исключающие позиционированные NaN
Все строки имеют NaN
, которые являются взаимоисключающими между столбцами.
df = pd.DataFrame({
'a': [1.0, 2.0, 3.0, np.nan, 5.0, np.nan, np.nan],
'b': [np.nan, np.nan, np.nan, 4.0, np.nan, 6.0, 7.0]})
df
a b
0 1.0 NaN
1 2.0 NaN
2 3.0 NaN
3 NaN 4.0
4 5.0 NaN
5 NaN 6.0
6 NaN 7.0
Series.update
Этот метод работает на месте, изменяя оригинальный DataFrame. Это эффективный вариант для этого варианта использования.
df['b'].update(df['a'])
# Or, to update "a" in-place,
# df['a'].update(df['b'])
df
a b
0 1.0 1.0
1 2.0 2.0
2 3.0 3.0
3 NaN 4.0
4 5.0 5.0
5 NaN 6.0
6 NaN 7.0
Series.add
df['a'].add(df['b'], fill_value=0)
0 1.0
1 2.0
2 3.0
3 4.0
4 5.0
5 6.0
6 7.0
dtype: float64
DataFrame.fillna
+ DataFrame.sum
df.fillna(0).sum(1)
0 1.0
1 2.0
2 3.0
3 4.0
4 5.0
5 6.0
6 7.0
dtype: float64
Ответ 4
Я столкнулся с этой проблемой, но хотел объединить несколько столбцов, выбирая первый ненулевой из нескольких столбцов. Я нашел следующее полезное:
Создание фиктивных данных
import pandas as pd
df = pd.DataFrame({'a1': [None, 2, 3, None],
'a2': [2, None, 4, None],
'a3': [4, 5, None, None],
'a4': [None, None, None, None],
'b1': [9, 9, 9, 999]})
df
a1 a2 a3 a4 b1
0 NaN 2.0 4.0 None 9
1 2.0 NaN 5.0 None 9
2 3.0 4.0 NaN None 9
3 NaN NaN NaN None 999
объединить a1 a2, a3 в новый столбец A
def get_first_non_null(dfrow, columns_to_search):
for c in columns_to_search:
if pd.notnull(dfrow[c]):
return dfrow[c]
return None
# sample usage:
cols_to_search = ['a1', 'a2', 'a3']
df['A'] = df.apply(lambda x: get_first_non_null(x, cols_to_search), axis=1)
print(df)
a1 a2 a3 a4 b1 A
0 NaN 2.0 4.0 None 9 2.0
1 2.0 NaN 5.0 None 9 2.0
2 3.0 4.0 NaN None 9 3.0
3 NaN NaN NaN None 999 NaN
Ответ 5
Я думаю, что такое решение,
def coalesce(s: pd.Series, *series: List[pd.Series]):
"""coalesce the column information like a SQL coalesce."""
for other in series:
s = s.mask(pd.isnull, other)
return s
потому что, имея DataFrame со столбцами с ['a', 'b', 'c']
, вы можете использовать его как объединение SQL,
df['d'] = coalesce(df.a, df.b, df.c)
Ответ 6
Для более общего случая, когда нет NaN, но вы хотите того же поведения:
Объединить "влево", но переопределить "вправо" # #; значения, где это возможно