通过替换迭代来加快熊猫代码

时间:2019-08-23 15:48:19

标签: python pandas

我有一个如下所示的数据框

+-----------+----------+-------+-------+-----+----------+-----------+
| 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的{​​{1}}值。 从我为question获得的答案中得到启发后,我设法产生了下面提到的所需输出

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

但是数据包含数百万行,并且此代码运行非常缓慢。经过简短的Google搜索和其他成员的评论,我知道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()以使我的代码更高效,更Python化?

2 个答案:

答案 0 :(得分:1)

这适用于示例数据。它对您的实际数据有效吗?

# 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

我们首先要获得ProdTotalInvoiceNo的总计MainCode(但仅在MainCode不等于零的情况下,{{1} }):

.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中的Item#匹配result中的MainCode。一种方法是更改​​subtotal中的列名,然后执行左合并:

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 和新的price重新计算qty

ProdTotal

答案 1 :(得分:-1)

大熊猫合并。将数据分成两个数据框,一个包含发票,total_amt,物品编号价格,数量,另一个包含发票,主代码。使用合并操作执行内部联接,之后您可以按行对列的值求和,然后删除不需要的列。