簡體   English   中英

如果 pandas 列值等於另一列的值,則更新它們

[英]Update pandas column values if they are equal to another column's value

我有一個 pandas 數據框,其中包含帳戶 ID、家庭、工作和手機號碼。 所有這些值都是字符串。 我的目標是更新每一行的值,使同一行和不同行中的所有重復數字都設置為 NaN,留下一個“原始”數字。 我怎樣才能有效地完成這項工作?

更新同一行中的值時,優先考慮家庭電話,然后是工作電話。 因此,如果home == work == mobile ,則 work 和 mobile 都會更新為 NaN。 如果home != work == mobile ,則 mobile 更新為 NaN。 更新不同行中的值時,將哪個重復電話號碼保留為“原始”號碼並不重要。 例如,如果A['home'] == B['mobile'] == C['work'] ,其中兩個值應設置為 NaN,其余一個保持不變。 在顯示數據框時,我選擇保留第一個數字並將其他重復數字設置為 NaN。

我已經想出如何使用df.loc更新同一行內的值,但我一直沒能弄清楚如何將不同行和列的重復值更新為 NaN。 我怎樣才能做到這一點?

以下是有關我正在嘗試做什么以及我遇到困難的更多信息:

我的初始數據框看起來像這樣:

acct_id        home        work      mobile
      A  1111111111  1111111111  1111111111
      B  2222222222  2222222222  2222222222
      C  3333333333  3333333333  3333333333
      D  4444444444  5555555555  5555555555
      E  6666666666  7777777777  8888888888
      F  9999999999  9999999999  8888888888
      G  7777777777  6666666666  5555555555
      H  4444444444  3333333333  2222222222
      I         NaN         NaN         NaN

我的目標是更新數據框,使其看起來像這樣:

acct_id        home        work      mobile
      A  1111111111         NaN         NaN
      B  2222222222         NaN         NaN
      C  3333333333         NaN         NaN
      D  4444444444  5555555555         NaN
      E  6666666666  7777777777  8888888888
      F  9999999999         NaN         NaN
      G         NaN         NaN         NaN
      H         NaN         NaN         NaN
      I         NaN         NaN         NaN

我目前正在將其作為兩步問題來處理。 第 1 步是刪除同一行中的重復數字。 第 2 步是刪除跨不同行和不同列的重復數字。 我已經找到了第 1 步,使用df.loc命令:

df.loc[df['home'] == df['work'], ['work']] = np.nan
df.loc[df['home'] == df['mobile'], ['mobile']] = np.nan
df.loc[df['work'] == df['mobile'], ['mobile']] = np.nan

這是運行上述命令后我的數據框的樣子:

acct_no        home        work      mobile
      A  1111111111         NaN         NaN
      B  2222222222         NaN         NaN
      C  3333333333         NaN         NaN
      D  4444444444  5555555555         NaN
      E  6666666666  7777777777  8888888888
      F  9999999999         NaN  8888888888
      G  7777777777  6666666666  5555555555
      H  4444444444  3333333333  2222222222
      I         NaN         NaN         NaN

但是,我無法繞過第 2 步。作為一種蠻力方法,我發現我可以對 home 上的數據幀進行排序,然后遍歷每一行,檢查前一行的 home 值是否與當前行相同行的,如果當前行的值相同,則將其設置為 nan。 最后,我將不得不為工作密鑰和移動密鑰重復該過程。 這是檢查主場的代碼的樣子:

df.sort_values(by='home', inplace=True)
prev_row = {'home':None,'work':None,'mobile':None}
    for cur_idx,cur_row in df.iterrows():
        if prev_row['home'] == cur_row['home']:
            cur_row['home'] = np.nan
        prev_row = cur_row

在運行上面的代碼只是為了更新和檢查主頁列之后,我的數據框將如下所示:

acct_no        home        work      mobile
      A  1111111111         NaN         NaN
      B  2222222222         NaN         NaN
      C  3333333333         NaN         NaN
      D  4444444444  5555555555         NaN
      E         NaN  3333333333  2222222222
      F  6666666666  7777777777  8888888888
      G  7777777777  6666666666  5555555555
      H  9999999999         NaN  8888888888
      I         NaN         NaN         NaN

這個解決方案非常笨拙,對於較大的數據集效率不高,那么我怎樣才能以更有效的方式實現這一目標呢?

非常感謝任何幫助 - 在此先感謝您!

這可能會解決您第 2 步的需求。 如果沒有,請隨意采用另一種方法。

df = pd.DataFrame(
    [
        dict(acct_no="D", home="4444444444", work="5555555555"),
        dict(acct_no="E", home=np.NaN, work="3333333333", mobile="2222222222"),
        dict(acct_no="J", home=np.NaN, work=np.NaN, mobile="8888888888"),
        dict(acct_no="K", home=np.NaN, work="8888888888"),
        dict(acct_no="L", home=np.NaN, work=np.NaN, mobile="8888888888"),
    ]
)
df["phone"] = (df.home
               .combine_first(df.work)
               .combine_first(df.mobile))
df = (df.sort_values(by="phone")
      .drop_duplicates(subset="phone")
      .set_index("acct_no"))
print(df)

輸出

               home        work      mobile       phone
acct_no                                                
E               NaN  3333333333  2222222222  3333333333
D        4444444444  5555555555         NaN  4444444444
J               NaN         NaN  8888888888  8888888888

在此實現中,我們查看phone列,這是帳戶的首選號碼。 這可能比期望的更嚴厲一些。 例如,請注意帳戶“K”和“L”在與“J”共享電話號碼的基礎上完全被刪除。 如果多個客戶共享一條家庭固定電話,那可能不是所需的業務邏輯。 另請注意,如果“K”要添加家庭號碼 7878787878,他將存活下來,盡管 8888888888 dup。 如果移動比家庭“更獨特”,也許我們應該更喜歡那個數字。

現在我們已經充分利用了phone列,可以隨意使用 .drop() 了。

排序成本為 O(N log N),其他一切都是線性的,因此這應該是一個高性能的解決方案,即使對於大型數據集也是如此。

在我看來,最簡單的方法是將數字stack為 Series、 maskdrop_duplicates ,然后恢復原始形狀:

out = (df.set_index('acct_id').stack()
         # the magic happens here
         .mask(lambda d: d.duplicated())
         # restore original format
         .unstack().reset_index()
       )

選擇:

out = (df.set_index('acct_id')
         .stack().drop_duplicates().unstack()
         .reindex(df['acct_id']).reset_index().reindex(columns=df.columns)
      )

輸出:

  acct_id        home        work      mobile
0       A  1111111111         NaN         NaN
1       B  2222222222         NaN         NaN
2       C  3333333333         NaN         NaN
3       D  4444444444  5555555555         NaN
4       E  6666666666  7777777777  8888888888
5       F  9999999999         NaN         NaN
6       G         NaN         NaN         NaN
7       H         NaN         NaN         NaN
8       I         NaN         NaN         NaN

順序:列在前

如果你願意,你可以輕松地調整上面的內容,以在選擇要保留的重復項的方式中給出列優先的偏好:

out = (df.set_index('acct_id').unstack()
         .mask(lambda d: d.duplicated())
         .swaplevel().unstack().reset_index() # or: .unstack().T.reset_index()
      )

輸出:

  acct_id        home        work      mobile
0       A  1111111111         NaN         NaN
1       B  2222222222         NaN         NaN
2       C  3333333333         NaN         NaN
3       D  4444444444  5555555555         NaN
4       E  6666666666         NaN  8888888888
5       F  9999999999         NaN         NaN
6       G  7777777777         NaN         NaN
7       H         NaN         NaN         NaN
8       I         NaN         NaN         NaN

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM