pandas 遍历_pandas修改数据时要注意的问题

bdb43430bc79690437880e7c03c0bc81.png

4193922966b6a079bd3985ed7e1d7433.png

导入数据:

import numpy as np
import pandas as pd
import datetime

df=pd.read_excel('/Users/dxn/Desktop/发票2020.xlsx',
                       header=0,dtype={'发票号码':'str'},index_col=[0,1])

>>> df
     日期 供货单位         发票号码    价税合计
0    一月    a     13599232    6450
4    二月    a     13599232    4800
2    三月    a     13599232    4800
11   四月    d  00192513-16  380000
5    五月    e     48693766    9500
7    六月    f     43000223    6400
1    七月    g     04922798     258
6    八月    g     04922798     280
3    九月   bb     04922798     406
10   十月   bb     04922798      36
8   十一月   bb     04922798      48
9   十二月   hh     17620827     600

现在要求,把数据中价税合计小于4000的全部修改为1000。

for idx,row in df.iterrows():
    if row['价税合计']<4000:
        df.iloc[idx]['价税合计']=1000
        
Warning (from warnings module):
  File "/Users/dxn/Desktop/遍历修改df数据.py", line 16
    df.iloc[idx]['价税合计']=1000
SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy

e4458190e2aa174b8b8b1c324e692f72.png

那么,正确的做法是什么?

我们需要明白以下几个概念。

一、我们要修改目标数据,首先我们要选中目标数据,然后对其进行操作以完成任务。修改数据即赋值,就是重新设置某些变量值的操作,又称为set;返回某些值的操作即访问,又称为get。任何引用数据子集的访问或赋值方法,称为索引;连续使用多个索引的操作,称为链式索引,这意味着对get或set进行多次调用以完成单个操作。

二、pandas中的'view'与'copy'。在pandas中,某些操作有时会返回数据的视图,也就是上面所说的view,有时会返回数据的副本,也就是copy。view是原始数据的子集,copy则是在原始数据的基础上创建的一个新的对象。那么,如果在并不能保证所修改的是view还是copy的时候,我们的操作就会返回不能预见的结果(writing to it will have no effect)。

具体到本例来说:
df.iloc[idx]['价税合计']=1000,这一句里第一个索引是为了get,获取一个DataFrame,其中包含索引为idx的所有行内容;第二个索引是为了set,是在第一个索引下返回的新DataFrame上设置变量值:我们并没有修改到原始的df里的值。

对此问题的解决方法,是用.loc方法将两次索引操作组合成一步操作,确保set操作的是原始的DataFrame,实现修改之所需要修改的数据。

for idx,row in df.iterrows():
	if row['价税合计']<4000:
		df.loc[df['价税合计']==row['价税合计'],'价税合计']=1000
>>> df
     日期 供货单位         发票号码    价税合计
0    一月    a     13599232    6450
1    七月    g     04922798    1000
2    三月    a     13599232    4800
3    九月   bb     04922798    1000
4    二月    a     13599232    4800
5    五月    e     48693766    9500
6    八月    g     04922798    1000
7    六月    f     43000223    6400
8   十一月   bb     04922798    1000
9   十二月   hh     17620827    1000
10   十月   bb     04922798    1000
11   四月    d  00192513-16  380000

获得预期的结果,成功!!!
据大佬们说,以上做法其实并不pandas,没有真正get到pandas处理数据的精要所在。

看批量处理数据时可供备选方法的优先顺序,Pandas核心开发人员给的建议:

  • 使用向量化操作:不用for遍历的Pandas方法和函数。
  • 使用.apply()方法。
  • 使用.itertuples():将DataFrame行作为nametuple类从Python的collections模块中进行迭代。
  • 使用.iterrows():将DataFrame行作为(index,pd.Series)元组数组进行迭代。虽然Pandas的Series是一种灵活的数据结构,但将每一行生成一个Series并且访问它,仍然是一个比较大的开销。
  • 对逐个元素进行循环,使用df.loc或者df.iloc对每个单元格或者行进行处理。

8f5ae23e58c1499869c1fb1a23d7e839.png

不用循环的两种方法:

  • .loc[]方法
df.loc[df['价税合计']<4000,'价税合计']=1000
#把某一列中符合条件的记录查找出来并修改本列或其他列的数据
#选取(get)目标数据,然后修改(set),确保修改之所需要修改的数据
  • apply方法
df['价税合计']=df['价税合计'].apply(lambda x:x if x>4000 else 1000)

pandas擅长向量式的批量处理数据,并不优先选用遍历循环方式实现数据的处理,这是与讲究“遍历在手天下我有”的VBA不同的地方。有没有一种拨云见日的清爽?

00bbeee6dd09504a92cc33575b7c47fb.png
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值