[英]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
我們首先要獲取每個InvoiceNo
和MainCode
的匯總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.