当前位置: 首页 > 科技观察

使用pandas对数据进行移动计算_0

时间:2023-03-14 00:17:18 科技观察

假设有10天的销售数据,我们要每三天计算一次总和,比如第五天的总和就是第三天的销售额总和+第四天+第五天还有,这个时候我们该怎么办呢?Series对象有一个rolling方法,专门用于移动计算。让我们来看看。将熊猫导入为pdamount=pd.Series([100,90,110,150,110,130,80,90,100,150])print(amount.rolling(3).sum())"""0NaN#NaN+NaN+1001NaN#NaN+100+902300.0#100+90+1103350.0#90+110+1504370.0#110+150+1105390.0#150+110+130630.0+3030#0+908270.0#80+90+1009340.0#90+100+150dtype:float64"""结果和我们想要的一样,amount.rolling(3)相当于创建一个长度为3的窗口,window从上往下滑动,我们画了一张图:amount.rolling(3)做了类似图片的事情,然后在它的基础上调用sum,会把每个window里面的元素累加起来,也就是Getthe以上代码的输出。另外,窗口的大小可以是任意的,这里我们以3为例。除了求和,还可以计算平均值,计算方差等,可以进行很多操作。如果你有兴趣,你可以自己尝试一下。当然我们也可以自定义函数:importpandasaspdimportnumpyasnpamount=pd.Series([100,90,110,150,110,130,80,90,100,150])print(#调用aggmethod,passAfunction#参数x为每个窗口中元素组成的Series对象amount.rolling(3).agg(lambdax:np.sum(x)*2))"""0NaN#(NaN+NaN+100(150+110+130)*26640.0#(110+130+80)*27600.0#(130+80+90)*28540.0#(80+90+100)*29680.0#(90+100)+150)*2dtype:float64"""agg中函数的逻辑可以是任意的,但是返回必须是一个值。另外,我们注意到前两个元素是NaN,这是因为rolling(3)的意思从当前位置往上过滤,一共筛选出3个元素,图中已经画的很清楚了,但是如果元素不够,我们要统计多少个元素呢?例如:元素之和第一个窗口中的是第一个元素,第一个窗口中的元素之和第二个窗口是第一个元素加上第二个元素。importpandasaspdamount=pd.Series([100,90,110,150,110,130,80,90,100,150])print(#min_periods表示窗口的最小观测值amount.rolling(3,min_periods=1).sum())"""0100.01190.02300.03350.04370.05390.06320.07300.08270.09340.0dtype:float64"""可以通过加一个min_periods参数来实现,min_periods代表窗口的最小观测值,即,窗口中元素的最小数量,默认等于窗口的长度。我们的窗口长度是3,但是min_periods指定为1,也就是说元素不够没有关系,只要有一个就可以了。因此,如果没有足够的元素,有几个。如果我们将min_periods指定为2会怎样?很明显第一个是NaN,第二个还是190.0,因为窗口的元素个数至少是2个。importpandasaspdamount=pd.Series([100,90,110,150,110,130,80,90,100,150])print(#窗口的最小观测值为2amount.rolling(3,min_periods=2).sum())"""0NaN1190.02300.03350.04370.05390.06320.07300.08270.09340.0dtype:float64"""注意:min_periods必须小于等于窗口长度,否则会报错。rolling中还有一个center参数,默认为False。我们知道rolling(3)的意思是从当前元素开始往上过滤,再加上它自己一共过滤3个元素。但是如果center指定为True,则当前元素将居中并从两个方向进行过滤。比如rolling(3,center=True),那么会向上选一个,向下选一个,一共3个。所以原理图看起来像这样:让我们来测试一下:importpandasaspdamount=pd.Series([100,90,110,150,110,130,80,90,100,150])print(amount.rolling(3、center=True).sum())"""0NaN1300.02350.03370.04390.05320.06300.07270.08340.09NaNdtype:float64"""这里没有指定min_periods,最小观测值等于窗口长度,所以滚动(3,center=True)会导致开头为NaN,结尾为NaN。这时候,有些人可能会好奇。如果窗口的长度是奇数,就很简单了。比如长度是9,那么选4向上,4向下,把自己加到刚好9。但是如果窗口的长度是偶数呢?比如长度为8,此时会选择上4下3,加上自己恰好8。另外,我们还可以从上到下过滤,比如窗口长度为3,但是我们要从当前元素开始过滤到底,一共加3来过滤。将熊猫作为ppdf从pandas.api.indexers导入FixedForwardWindowIndexeramount=pd.Series([100,90,110,150,110,130,80,90,100,150])print(amount.rolling(FixedForwardWindowIndexer(window_size=3)).sum())"""0300.01350.02370.03390.04320.05300.06270.07340.08NaN9NaNdtype:float64"""这个可以通过类FixedForwardWindowIndexer来实现,当然此时不能指定center参数。调用amount.rolling()会返回一个Rolling对象,然后调用Rolling对象的sum、max、min、mean、std等方法计算每个窗口的sum、maximum、minimum等。当然我们也可以调用agg方法,传入一个函数来定义每个窗口的计算逻辑。那么重点就是agg除了可以接收一个函数,还可以接收一个列表。列表中可以有多个函数,然后同时执行多个操作。importpandasaspdimportnumpyasnpamount=pd.Series([100,90,110,150,110,130,80,90,100,150])print(amount.rolling(3).agg([np.sum,np.mean,lambdax:np.sum(x)*2]))#Performmultipleoperations,thenaDataFramewillbereturned"""summean0NaNNaNNaN1NaNNaNNaN2300.0100.000000600.03350.0116.666667700.04370.0123.333333740.05390.0130.000000780.06320.0106.666667640.07300.0100.000000600.08270.090.000000540.09340.0113.333333680.0"""除了Series之外,DataFrame也有rolling方法,功能和用法是一样的,只不过后者可以同时作用于多列。Butmostofthetime,wecalltherollingmethodoftheSeriesobject.Therollingmethodalsohasapowerfulfunction,thatis,itcananalyzethemovementoftime,becausepandasitselfwasborninthefinancialfield,soitisverygoodatmanipulatingtime.Sowhataretheusagescenariosfortimemovementanalysis?Letmegiveyouaproblemthattheauthorencounteredduringhissenioryearinternship.Atthattime,hewasusingpandasforauditing,andheencounteredsucharequirement:todeterminewhethertherearemorethan1000rechargetimeswithin30seconds(thatis,todetectwhethertherearealargenumberofrechargesatthesametime).recharge),ifthereare,findthem.Becauseeachrechargecorrespondstoarecord,andeachrecordhasaspecifictime,inotherwords,itisnecessarytojudgewhetherthereisacertain30seconds,inwhichtherearemorethan1000records.Iwasjustaninternatthattime,andIwasdirectlyconfusedbythisproblem,butwiththerollingmethod,itbecamemuchsimpler.将熊猫导入为pdamount=pd.Series([100,100,100,100,100,100,100,100,100,100],index=pd.DatetimeIndex(["2020-1-1","2020-1-3","2020-1-4","2020-1-6","2020-1-7","2020-1-9","2020-1-12","2020-1-13","2020-1-14","2020-1-15"]))print(金额)"""2020-01-011002020-01-031002020-01-041002020-01-061002020-01-071002020-01-091002020-01-121002020-01-131002020-01-141002020-01-15100dtype:int64"""#这里我们还是计算3天之内的和#为了简单直观我们改值到100print(amount.rolling("3D").sum())"""2020-01-01100.02020-01-03200.02020-01-04200.02020-01-06200.02020-01-07200.02020-01-09200.02020-01-12100.02020-01-13200.02020-01-14300.02020-01-15300.0dtype:float64"""我们来分析一下,首先rolling("3D")表示3天内过滤,如果是是分析时间的运动,那么索引必须是datetime类型。先看2020-01-01,上面没有记录,所以是100(此时没有NaN);然后是2020-01-03,因为上面的2020-01-01和它之间相隔不超过3天,所以总数是200;再看2020-01-12,因为只能找到2020-01-10、2020-01-11,然后加在一起。但是是2020-01-09,已经3天多了,所以结果是100(就是自己);最后看2020-01-14,如果在3天内,应该是2020-01-12、2020-01-13,加上自己的2020-01-14,所以结果是300。同理2020-01-15。怎么样,是不是很简单?回到作者原问题,如果想在30秒内查找1000条以上的记录,以交易时间为索引,直接roll("30S").count()。然后找出大于1000的记录,说明这条记录上面的第1000条记录的事务时间与这条记录的事务时间差的绝对值不超过30秒(记录按照事务排序时间)。至于这30秒内进行了多少笔交易,只要将记录的交易时间减去30秒,过滤即可。所以用rolling的方式来处理这个问题是很方便的,但是当时不知道,就傻傻地写了一个for循环,一个一个遍历。另外,估计有些人对pandas中代表时间的符号不是很清楚。最重要的是,它们在格式化时很容易与Pythondatetime使用的符号混淆。下面就来区分一下。你感觉如何?它易于使用且功能强大吗?