2017-04-03 48 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