Python 通过替换iterrows加快pandas代码的速度

Python 通过替换iterrows加快pandas代码的速度,python,pandas,Python,Pandas,我有一个如下所示的数据帧 +-----------+----------+-------+-------+-----+----------+-----------+ | InvoiceNo | totalamt | Item# | price | qty | MainCode | ProdTotal | +-----------+----------+-------+-------+-----+----------+-----------+ | Inv_001 | 1720 |

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

+-----------+----------+-------+-------+-----+----------+-----------+
| 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
等于
项。
从我为我的工作得到的答案中得到启发,我成功地产生了下面提到的期望输出

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

然后我们需要从主数据帧中过滤这些数据,所以我们只需过滤
MainCode
等于零的地方,
.eq(0)

我们希望将小计加入到该结果中,
InvoiceNo
匹配,
result
中的
Item#
subtotal
中的
MainCode
匹配。一种方法是更改
小计
中的列名,然后执行左合并:

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
最后,我们重新计算
价格
,给定
数量
和新的
产品总数

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

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

# 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)
):

然后我们需要从主数据帧中过滤这些数据,所以我们只需过滤
MainCode
等于零的地方,
.eq(0)

我们希望将小计加入到该结果中,
InvoiceNo
匹配,
result
中的
Item#
subtotal
中的
MainCode
匹配。一种方法是更改
小计
中的列名,然后执行左合并:

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
最后,我们重新计算
价格
,给定
数量
和新的
产品总数

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

不要合并熊猫。将数据拆分为两个数据框,一个包含发票、总金额、项目、价格、数量,另一个包含发票、主代码。使用合并操作执行内部联接,然后可以按行求和列的值,并删除不需要的列。

执行合并操作。将数据拆分为两个数据框,一个包含发票、总金额、项目、价格、数量,另一个包含发票、主代码。“使用合并操作进行内部联接”,之后可以按行求和列的值,并删除不需要的列。

为什么不为零,所有行的主代码都不等于项目编号。”。你是说分组相加吗?什么应该是零@如果6
MainCode
等于每个
InvoiceNo
中的
。例如:在
Inv_001
中,
项777 888
具有
MainCode 260
。因此,这些项是
260
的一部分,但在您的示例数据框中,
MainCode
值中没有一个等于
Item.
@HS nebula column
Item.
。将其视为主要产品和其他项目(例如在
Inv_001
777、888
Inv_002
777 888 999
中)是与此相关联的子产品。为什么不是零,没有一行的主代码与项目编号相同。你是说分组相加吗?什么应该是零@如果6
MainCode
等于每个
InvoiceNo
中的
。例如:在
Inv_001
中,
项777 888
具有
MainCode 260
。因此,这些项是
260
的一部分,但在您的示例数据框中,
MainCode
值中没有一个等于
Item.
@HS nebula column
Item.
。将其视为主要产品,其他项目(如
Inv_001
777、888
Inv_002
777 888 999
)是与此相关的子产品。在示例数据中,它运行良好。但是,在原始数据中并非如此。让我进一步检查我的代码&会给你回复的。谢谢……太好了。现在快了3倍。非常感谢您建议避免提供此解决方案。
iterrows()
&在示例数据中效果良好。但是,在原始数据中并非如此。让我进一步检查我的代码&会给你回复的。谢谢……太好了。现在快了3倍。非常感谢您建议避免提供此解决方案。
iterrows()
&我不明白为什么对我的答案投反对票。我不明白为什么对我的答案投反对票。