簡體   English   中英

通過替換迭代來加快熊貓代碼

[英]Speeding up pandas code by replacing iterrows

我有一個如下的數據框

+-----------+----------+-------+-------+-----+----------+-----------+
| InvoiceNo | totalamt | Item# | price | qty | MainCode | ProdTotal |
+-----------+----------+-------+-------+-----+----------+-----------+
| Inv_001   |     1720 |   260 |  1500 |   1 |        0 |      1500 |
| Inv_001   |     1720 |   777 |   100 |   1 |      260 |       100 |
| Inv_001   |     1720 |   888 |   120 |   1 |      260 |       120 |
| Inv_002   |     1160 |   360 |   700 |   1 |        0 |       700 |
| Inv_002   |     1160 |   777 |   100 |   1 |      360 |       100 |
| Inv_002   |     1160 |   888 |   120 |   1 |      360 |       120 |
| Inv_002   |     1160 |   999 |   140 |   1 |      360 |       140 |
| Inv_002   |     1160 |   111 |   100 |   1 |        0 |       100 |
+-----------+----------+-------+-------+-----+----------+-----------+

我想添加ProdTotal值,其MainCode等於Item# 從我對問題的回答中得到啟發,我設法產生了下面提到的期望輸出

+-----------+----------+-------+-------+-----+----------+-----------+
| InvoiceNo | totalamt | Item# | price | qty | MainCode | ProdTotal |
+-----------+----------+-------+-------+-----+----------+-----------+
| Inv_001   |     1720 |   260 |  1720 |   1 |        0 |      1720 |
| Inv_002   |     1160 |   360 |  1060 |   1 |        0 |      1060 |
| Inv_002   |     1160 |   111 |   100 |   1 |        0 |       100 |
+-----------+----------+-------+-------+-----+----------+-----------+

使用下面的代碼

df = pd.read_csv('data.csv')
df_grouped = dict(tuple(df.groupby(['InvoiceNo'])))

remove_index= []
ids = 0

for x in df_grouped:
    for index, row in df_grouped[x].iterrows():
        ids += 1
        try:
            main_code_data = df_grouped[x].loc[df_grouped[x]['MainCode'] == row['Item#']]
            length = len(main_code_data['Item#'])
            iterator = 0
            index_value = 0    
            for i in range(len(df_grouped[x].index)):
                index_value += df_grouped[x].at[index + iterator, 'ProdTotal']
                df.at[index, 'ProdTotal'] = index_value

                iterator += 1

            for item in main_code_data.index:
                remove_index.append(item)

        except:
            pass

df = df.drop(remove_index)

但是數據包含數百萬行,並且此代碼運行非常緩慢。 一個簡短的谷歌搜索和其他成員的評論,我知道iterrows()使代碼運行緩慢。 如何替換iterrows()使我的代碼更高效,更pythonic?

這適用於示例數據。 它對您的實際數據有效嗎?

# Sample data.
df = pd.DataFrame({
    'InvoiceNo': ['Inv_001'] * 3 + ['Inv_002'] * 5,
    'totalamt': [1720] * 3 + [1160] * 5,
    'Item#': [260, 777, 888, 260, 777, 888, 999, 111],
    'price': [1500, 100, 120, 700, 100, 120, 140, 100],
    'qty': [1] * 8,
    'MainCode': [0, 260, 260, 0, 260, 260, 260, 0],
    'ProdTotal': [1500, 100, 120, 700 ,100 ,120, 140, 100]
})

subtotals = df[df['MainCode'].ne(0)].groupby(
    ['InvoiceNo', 'MainCode'], as_index=False)['ProdTotal'].sum()
subtotals = subtotals.rename(columns={'MainCode': 'Item#', 'ProdTotal': 'ProdSubTotal'})

result = df[df['MainCode'].eq(0)]
result = result.merge(subtotals, on=['InvoiceNo', 'Item#'], how='left')
result['ProdTotal'] += result['ProdSubTotal'].fillna(0)
result['price'] = result.eval('ProdTotal / qty')
result = result.drop(columns=['ProdSubTotal'])

>>> result
  InvoiceNo  totalamt  Item#   price  qty  MainCode  ProdTotal
0   Inv_001      1720    260  1720.0    1         0     1720.0
1   Inv_002      1160    260  1060.0    1         0     1060.0
2   Inv_002      1160    111   100.0    1         0      100.0

我們首先要獲取每個InvoiceNoMainCode的匯總ProdTotal (但僅在MainCode不等於零的情況下, .ne(0) ):

subtotals = df[df['MainCode'].ne(0)].groupby(
    ['InvoiceNo', 'MainCode'], as_index=False)['ProdTotal'].sum()
>>> subtotals
  InvoiceNo  MainCode  ProdTotal
0   Inv_001       260        220
1   Inv_002       260        360

然后,我們需要從主數據幀中過濾此數據,因此我們只過濾MainCode等於零的.eq(0)

result = df[df['MainCode'].eq(0)]
>>> result
  InvoiceNo  totalamt  Item#  price  qty  MainCode  ProdTotal
0   Inv_001      1720    260   1500    1         0       1500
3   Inv_002      1160    260    700    1         0        700
7   Inv_002      1160    111    100    1         0        100

我們希望將小計加入此結果,其中InvoiceNo匹配, result中的Item#匹配subtotal中的MainCode 一種方法是更改subtotal的列名,然后執行左合並:

subtotals = subtotals.rename(columns={'MainCode': 'Item#', 'ProdTotal': 'ProdSubTotal'})
result = result.merge(subtotals, on=['InvoiceNo', 'Item#'], how='left')
>>> result
  InvoiceNo  totalamt  Item#  price  qty  MainCode  ProdTotal  ProdSubTotal
0   Inv_001      1720    260   1500    1         0       1500         220.0
1   Inv_002      1160    260    700    1         0        700         360.0
2   Inv_002      1160    111    100    1         0        100           NaN

現在,我們將ProdSubTotal添加到ProdTotal並刪除該列。

result['ProdTotal'] += result['ProdSubTotal'].fillna(0)
result = result.drop(columns=['ProdSubTotal'])
>>> result
  InvoiceNo  totalamt  Item#  price  qty  MainCode  ProdTotal
0   Inv_001      1720    260   1500    1         0     1720.0
1   Inv_002      1160    260    700    1         0     1060.0
2   Inv_002      1160    111    100    1         0      100.0

最后,根據給定的qty和新的ProdTotal重新計算price

result['price'] = result.eval('ProdTotal / qty')
>>> result
  InvoiceNo  totalamt  Item#   price  qty  MainCode  ProdTotal
0   Inv_001      1720    260  1720.0    1         0     1720.0
1   Inv_002      1160    260  1060.0    1         0     1060.0
2   Inv_002      1160    111   100.0    1         0      100.0

熊貓合並。 將數據分成兩個數據框,一個包含發票,total_amt,物品編號價格,數量,另一個包含發票,主代碼。 使用合並操作執行內部聯接,之后您可以按行對列的值求和,然后刪除不需要的列。

暫無
暫無

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

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