2017-04-03 47 views
1

我不想要合并/连接列或用其他值替换某些值(尽管...也许是?)。但我有一个大的数据框(> 100行和列),我想提取“几乎相同”的列,即有> 2个值(在同一个索引处)共同的,而在其他索引处没有不同的值如果在一列中有值,则必须有相同的值或另一列中的NaN)。 下面是这样一个数据帧的例子:有没有办法获得几列熊猫DataFrame的“联盟”?

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 

EDIT

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,而在其他位置,一个有一个值,而另一个有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方式来做到这一点?即1)根据条件中的值必须是相同的或np.nan而不是不同的条件来查找列。 2)设置一个条件,即一个列不能合并,如果它具有与先前包含的值相同的索引开始(我可能需要将该字符串拆分为两列并执行multiindex ???)3)将它们合并成新的系列/数据帧。

回答

0

等瞧

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) 
+0

啊,先前的答案被删除(与我的评论一起):(所以再次:我想知道之前的那一步,即如何找出它是我想要的B和D列(搜索与np.nan具有相同或值的列 - 如果在同一个索引处有不同的值,我不希望这样)。谢谢。我将在问题中编辑它以使它更清晰 – durbachit

1
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 

它是如何工作

  • ij表示使用numpy得到一个上三角形的索引列的每个组合。
  • 切片底层numpy数组df.valuesij并将它们相减。如果差异是nan,意味着其中一个是nan。否则,如果各个元素相同,差异应该为零。
  • 因为我们可以容忍nan在一个或另一个中,使用np.where填充零。
  • 找到(x == 0).all(0)所有行都为零的位置。
  • 使用上面的掩码切片ij并确定匹配的列。
  • 为所有匹配的数据框建立一个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 

enter image description here

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 
+0

感谢您的示例和解释,这看起来正是我想要的!但是,不知何故,我实现失败。您的示例证明该方法的工作原理,并返回组合列的数据框但是,当我在数据上使用它时,它会返回一个非常大的数据帧。任何想法可能会导致这种差异? – durbachit

+0

@durbachit没有保证返回的数据帧是“更小”的。我假设你意味着更少的列。对于10列数据框,如果每列都匹配,则最终可能会得到45列结果。凭借足够的专栏和稀疏性,我不会期望列数很少。 – piRSquared

+0

哦,我明白了!不应该每个专栏都与其他专栏匹配,但有些情况下我可以在3-4栏之间进行匹配,而不仅仅是两篇。所以我想将所有这3列合并成一个。函数内部的'while'循环可以完成这项工作吗? (在计算d后,继续看看是否有更多的相同参数) – durbachit

1

这听起来像症结是如何检测 “几乎相同” 的列,它们是唯一的不同列(如果有的话)缺少什么值。给出两列名称,你如何检查它们是否几乎相同?请注意,如果我们发现有重要差异,则它必须位于索引中,这两列均不具有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 

在这里,您既可以合并BCA,但不能同时使用。除了这些问题之外,多个列可以合并为一个,因为合并列被保存在一个比较列中。

+0

酷!这个可以在多列上工作!然而,它并没有说明哪些是合并的......但是我可以创建一个列表,描述哪些与新数据帧结合在一起。干杯! – durbachit

+0

我怎么能在这里设置一个条件来不合并其索引包含字符串的相同部分的值? – durbachit

+0

说什么?我不确定你的意思,但如果你能检测到它,我认为它很清楚在哪里进行测试。解释你需要什么,如果它不够重要成为一个新问题,我会添加它。 – alexis