将 2 列中的值合并为 pandas 数据框中的单列

Posted

技术标签:

【中文标题】将 2 列中的值合并为 pandas 数据框中的单列【英文标题】:Coalesce values from 2 columns into a single column in a pandas dataframe 【发布时间】:2016-11-04 06:39:52 【问题描述】:

我正在寻找一种行为类似于 T-SQL 中的合并的方法。我有 2 列(A 列和 B 列)在 pandas 数据框中稀疏填充。我想使用以下规则创建一个新列:

    如果 A 列中的值不为空,则将该值用于新列 C 如果 A 列中的值为 null,则将 B 列中的值用于新的 C 列

就像我提到的,这可以在 MS SQL Server 中通过 coalesce 函数来完成。我还没有找到一个好的pythonic方法;有吗?

【问题讨论】:

【参考方案1】:

使用combine_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】:

使用DataFrame.bfill 合并多个列

所有这些方法都适用于两列,并且可能适用于三列,但如果您在 n > 2 时有 n 列,它们都需要方法链接:

示例数据框

import numpy as np
import pandas as pd

df = pd.DataFrame('col1':[np.NaN, 2, 4, 5, np.NaN],
                   'col2':[np.NaN, 5, 1, 0, np.NaN],
                   'col3':[2, np.NaN, 9, 1, np.NaN],
                   'col4':[np.NaN, 10, 11, 4, 8])

print(df)

   col1  col2  col3  col4
0   NaN   NaN   2.0   NaN
1   2.0   5.0   NaN  10.0
2   4.0   1.0   9.0  11.0
3   5.0   0.0   1.0   4.0
4   NaN   NaN   NaN   8.0

在索引轴 (axis=1) 上使用DataFrame.bfill,即使n 的列数很大,我们也可以通用方式获取值

另外,这也适用于string type 列!!

df['coalesce'] = df.bfill(axis=1).iloc[:, 0]

   col1  col2  col3  col4  coalesce
0   NaN   NaN   2.0   NaN       2.0
1   2.0   5.0   NaN  10.0       2.0
2   4.0   1.0   9.0  11.0       4.0
3   5.0   0.0   1.0   4.0       5.0
4   NaN   NaN   NaN   8.0       8.0

使用Series.combine_first(已接受的答案),它可能会变得非常麻烦,并且最终会在列数增加时无法撤消

df['coalesce'] = (
    df['col1'].combine_first(df['col2'])
        .combine_first(df['col3'])
        .combine_first(df['col4'])
)

   col1  col2  col3  col4  coalesce
0   NaN   NaN   2.0   NaN       2.0
1   2.0   5.0   NaN  10.0       2.0
2   4.0   1.0   9.0  11.0       4.0
3   5.0   0.0   1.0   4.0       5.0
4   NaN   NaN   NaN   8.0       8.0

【讨论】:

如果您需要按特定列顺序合并,只需选择您需要的列,如df['coalesce'] = df[['col4', 'col1', 'col2', 'col3']].bfill(axis=1).iloc[:, 0]【参考方案3】:

也试试这个.. 更容易记住:

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

【讨论】:

【参考方案4】:

combine_first 是最直接的选项。我在下面概述了其他几个。我将概述更多解决方案,其中一些适用于不同的情况。

案例 #1:非互斥 NaN

并非所有行都有 NaN,这些 NaNs 在列之间互斥。

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

所有行都有NaNs,列之间互斥。

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

【讨论】:

【参考方案5】:

我遇到了这个问题,但想合并多个列,从多个列中选择第一个非空值。我发现以下内容很有帮助:

构建虚拟数据

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

【讨论】:

【参考方案6】:

我正在考虑这样的解决方案,

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

因为给定一个带有['a', 'b', 'c'] 列的DataFrame,您可以像使用SQL 合并一样使用它,

df['d'] = coalesce(df.a, df.b, df.c)

【讨论】:

【参考方案7】:

对于更一般的情况,没有 NaN 但您想要相同的行为:

Merge 'left', but override 'right' values where possible

【讨论】:

【参考方案8】:

代码不错,python 3 有错别字,改正如下

    """coalesce the column information like a SQL coalesce."""
    for other in series:
        s = s.mask(pd.isnull, other)        
    return s

【讨论】:

【参考方案9】:

考虑使用 DuckDB 在 Pandas 上实现高效 SQL。它高性能、简单且功能丰富。 https://duckdb.org/2021/05/14/sql-on-pandas.html

示例数据框:

import numpy as np
import pandas as pd

df = pd.DataFrame('A':[1,np.NaN, 3, 4, 5],
                   'B':[np.NaN, 2, 3, 4, np.NaN])

使用 DuckDB 进行合并:

import duckdb
out_df = duckdb.query("""SELECT A,B,coalesce(A,B) as C from df""").to_df()
print(out_df)

输出:

     A    B    c
0  1.0  NaN  1.0
1  NaN  2.0  2.0
2  3.0  3.0  3.0
3  4.0  4.0  4.0
4  5.0  NaN  5.0

【讨论】:

以上是关于将 2 列中的值合并为 pandas 数据框中的单列的主要内容,如果未能解决你的问题,请参考以下文章

如何计算包含一组列中的值和 Pandas 数据框中另一列中的另一个值的行数?

将 Python 中的两个电子表格与 Pandas 合并,按“时间”列中最近的“时间”,XX:XX:XX 格式的值

用上一列中的值填充熊猫数据框中的“无”值

如何将 numpy 数组存储在 Pandas 数据框的列中?

如何将 numpy 数组存储在 Pandas 数据框的列中?

如何将数据框中的多列合并为 Pandas 日期时间格式