【问题标题】:Is there a way to get a "union" of several columns of pandas DataFrame?有没有办法获得几列熊猫数据框的“联合”?
【发布时间】:2017-08-28 23:26:09
【问题描述】:

我不是在寻找合并/连接列或用其他值替换某些值(尽管......也许是的?)。但是我有一个大数据框(> 100 行和列),我想提取“几乎相同”的列,即具有> 2 个共同值(在同一索引处)并且在其他索引处没有不同值(如果一列中有值,则另一列中必须有相同的值或 NaN)。 以下是此类数据框的示例:

a = np.random.randint(1,10,10)
b = np.array([np.nan,2,np.nan,3,np.nan,6,8,1,2,np.nan])
c = np.random.randint(1,10,10)
d = np.array([7,2,np.nan,np.nan,np.nan,6,8,np.nan,2,2])
e = np.array([np.nan,2,np.nan,np.nan,np.nan,6,8,np.nan,np.nan,2])
f = np.array([np.nan,2,np.nan,3.0,7,np.nan,8,np.nan,np.nan,2])
df = pd.DataFrame({'A':a,'B':b,'C':c,'D':d,'E':e, 'F':f})
df.ix[3:6,'A']=np.nan
df.ix[4:8,'C']=np.nan

编辑

keys=['S01_o4584','S02_o2531','S03_o7812','S03_o1122','S04_o5210','S04_o3212','S05_o4665','S06_o7425','S07_o3689','S08_o2371']
df['index']=keys
df = df.set_index('index')

             A    B    C    D    E    F
index                                  
S01_o4584  8.0  NaN  9.0  7.0  NaN  NaN
S02_o2531  8.0  2.0  5.0  2.0  2.0  2.0
S03_o7812  1.0  NaN  5.0  NaN  NaN  NaN
S03_o1122  NaN  3.0  6.0  NaN  NaN  3.0
S04_o5210  NaN  NaN  NaN  NaN  NaN  7.0
S04_o3212  NaN  6.0  NaN  6.0  6.0  NaN
S05_o4665  NaN  8.0  NaN  8.0  8.0  8.0
S06_o7425  1.0  1.0  NaN  NaN  NaN  NaN
S07_o3689  8.0  2.0  NaN  2.0  NaN  NaN
S08_o2371  3.0  NaN  9.0  2.0  2.0  2.0

如您所见,列 B、D (和新的 E) 在位置(索引)S02_o2531、S04_o3212、S05_o4665 和 S08_o2371 具有相同的值,而在其他位置,一个具有值,而其他有 s NaN。

我想要的输出是:

index   BD*E*
S01_o4584   7
S02_o2531   2
S03_o7812   NaN
S03_o1122   3
S04_o5210   NaN
S04_o3212   6
S05_o4665   8
S06_o7425   1
S07_o3689   2
S08_o2371   2

但是,我无法组合在索引的同一开头具有两个不同值的列:如您所见,列 F 也共享一些索引,但新的索引位于 S04_o5210,但先前的组合列已经在“S04_”(索引 S04_o3212)处具有值。

有没有一种合理的 Pythonic 方式来做到这一点? IE。 1)根据列中的值必须相同或np.nan,而不是不同的条件来查找列。 2)设置一个条件,如果一个列具有先前包含的值的索引的相同开头,则不能合并(我可能需要将字符串分成两列并执行多索引???) 3)将它们合并到新的系列/数据帧。

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    等等

    test = df.B == df.D
    df.loc[test,'myunion'] = df.loc[test, 'B']
    df.loc[!test ,'myunion'] = df.loc[!test, 'B'].fillna(0) + df.loc[!test, 'D'].fillna(0)
    

    【讨论】:

    • 啊,之前的答案被删除了(连同我的评论):(。所以再一次:我也想知道之前的步骤,即如何找出它是 B 和 D我要合并的列。(搜索值与 np.nan 相同或值相同的列 - 如果同一索引处有不同的值,我不希望这样。)谢谢。我会编辑它在问题中使其更清楚。
    【解决方案2】:
    def almost(df):
        i, j = np.triu_indices(len(df.columns), 1)
    
        v = df.values
    
        d = v[:, i] - v[:, j]
        m = (np.where(np.isnan(d), 0, d) == 0).all(0)
    
        return pd.concat(
            [
                df.iloc[:, i_].combine_first(
                    df.iloc[:, j_]
                ).rename(
                    tuple(df.columns[[i_, j_]])
                ) for i_, j_ in zip(i[m], j[m])],
            axis=1
        )
    
    almost(df)
    
         B
         D
    0  7.0
    1  2.0
    2  NaN
    3  3.0
    4  NaN
    5  6.0
    6  8.0
    7  1.0
    8  2.0
    9  2.0
    

    工作原理

    • i 和 j 表示每个列组合,使用 numpy 获取上三角形的索引。
    • 用i 和j 对底层numpy 数组df.values 进行切片并减去它们。如果差异为nan,则表示其中一个或另一个是nan。否则,如果各个元素相同,则差异应为零。
    • 因为我们可以容忍nan 在其中之一,所以使用np.where 将它们填充为零。
    • 使用(x == 0).all(0) 查找所有行都为零的位置。
    • 使用上面的掩码对i 和j 进行切片并识别匹配的列。
    • 使用pd.MultiIndex 为显示匹配内容的列构建所有匹配项的数据框。

    更酷的例子

    np.random.seed([3,1415])
    m, n = 20, 26
    df = pd.DataFrame(
        np.random.randint(10, size=(m, n)),
        columns=list('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
    ).mask(np.random.choice([True, False], (m, n), p=(.6, .4)))
    
    df
    

    almost(df)
    
          A         D    G    H    I         J    K     
          J    X    K    M    N    J    K    V    S    X
    0   6.0  7.0  3.0  NaN  4.0  6.0  NaN  6.0  NaN  7.0
    1   3.0  3.0  2.0  6.0  4.0  NaN  2.0  6.0  2.0  2.0
    2   3.0  0.0  NaN  2.0  4.0  3.0  NaN  3.0  4.0  0.0
    3   4.0  4.0  3.0  5.0  5.0  4.0  3.0  4.0  3.0  3.0
    4   7.0  NaN  NaN  7.0  3.0  7.0  NaN  7.0  NaN  NaN
    5   NaN  NaN  2.0  0.0  5.0  NaN  2.0  2.0  2.0  2.0
    6   NaN  8.0  NaN  NaN  9.0  2.0  2.0  1.0  NaN  8.0
    7   NaN  7.0  NaN  9.0  9.0  6.0  6.0  NaN  NaN  7.0
    8   NaN  NaN  8.0  3.0  1.0  NaN  NaN  NaN  4.0  NaN
    9   0.0  0.0  8.0  2.0  NaN  3.0  3.0  NaN  NaN  NaN
    10  0.0  0.0  NaN  6.0  1.0  NaN  NaN  8.0  NaN  NaN
    11  NaN  NaN  3.0  NaN  9.0  3.0  3.0  NaN  3.0  3.0
    12  5.0  NaN  NaN  NaN  6.0  5.0  NaN  5.0  8.0  NaN
    13  NaN  NaN  NaN  NaN  7.0  5.0  5.0  NaN  NaN  NaN
    14  NaN  NaN  6.0  4.0  8.0  8.0  8.0  NaN  0.0  NaN
    15  8.0  8.0  7.0  NaN  NaN  NaN  NaN  NaN  2.0  NaN
    16  4.0  4.0  4.0  4.0  9.0  9.0  9.0  6.0  4.0  NaN
    17  NaN  4.0  NaN  4.0  2.0  8.0  8.0  4.0  NaN  4.0
    18  NaN  NaN  2.0  7.0  NaN  NaN  NaN  NaN  NaN  NaN
    19  NaN  7.0  6.0  3.0  5.0  NaN  NaN  7.0  NaN  7.0
    

    【讨论】:

    • 感谢您的示例和解释,这看起来正是我想要的!但是,不知何故,我在实施中失败了。您的示例证明该方法有效,并且它返回组合列的数据框(其列比原始列少)。但是,当我在数据上使用它时,它会返回一个更大的数据帧。有什么想法可能导致这种差异吗?
    • @durbachit 不能保证返回的数据帧“更小”。我假设您的意思是更少的列。对于 10 列数据框,如果每列相互匹配,您最终可能会得到 45 列结果。有了足够的列和稀疏性,我根本不希望有少量的列。
    • 哦,我明白了!不应该是每一列都相互匹配,但在某些情况下,我在 3-4 列之间匹配,而不仅仅是两列。因此,我想将所有这 3 列合并为一列。函数内部的while 循环可以完成这项工作吗? (计算d后,继续看是否有更多具有相同参数的)
    • @durbachit 我会小心的。这不是等价关系。意思是,它不是传递的。这意味着您可以让 A 匹配 B、B 匹配 C 和 C 不匹配 A。
    【解决方案3】:

    听起来关键点是如何检测“几乎相同”的列,这些列仅在缺失的值方面有所不同(如果有的话)。给定两个列名,如何检查它们是否几乎相同?请注意,如果我们发现一个重要的差异,它必须位于两个列都没有NaN 的索引处。换句话说,诀窍是丢弃具有缺失值的行并比较其余行:

    tocheck = df[["B", "D"]].dropna()
    if all(tocheck.B == tocheck.D):
        print("B, D are almost identical")
    

    让我们用它来遍历所有列对,并合并匹配的列:

    for a, b in itertools.combinations(df.columns, 2):
        if a not in df.columns or b not in df.columns:  # Was one deleted already?
            continue
        tocheck = df[[a, b]].dropna()
        if all(tocheck[a] == tocheck[b]):
            print(b, "->", a)
            df[a] = df[a].combine_first(df[b])
            del df[b]
    

    请注意(如果您没有注意到),当最终合并多个列时,可能会出现与顺序相关的行为。例如:

         A    B   C
    0   NaN   1   2 
    1   10   NaN NaN
    

    在这里,您可以将B 或C 合并到A,但不能同时合并。除了这些问题,多列可以合并为一列,因为合并的列被保存在比较列之一的位置。

    【讨论】:

    • 酷!这个适用于多个列!然而,它并没有说哪些被组合了......但我可以创建一个列表,描述哪些与新数据框组合在一起。干杯!
    • 如何在此处设置条件以不合并索引包含字符串相同部分的值?
    • 说什么?我不确定你的意思,但如果你能检测到它,我认为在哪里进行测试很清楚。说明你需要什么,如果它不够重要,不能成为一个新问题,我会添加它。
    • 我知道,对不起,这很难解释,我希望这个例子现在能说明问题。 (我编辑了问题)
    猜你喜欢
    • 2019-06-29
    • 2019-09-22
    • 2018-03-01
    • 2020-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-06
    • 2019-06-05
    相关资源
    最近更新 更多