算法优化---向量数组计算替代判断

  • 目录
    • 前言
    • 元素级别迭代与series,ndarray的迭代
    • 测试
    • series, ndarray之间的数学计算,替代元素遍历判断。
    • 根据数量级大小,选择适合的算法
    • *补充:感谢周老师~!

目录

前言

接着之前降到运行pandas效率(时间复杂度)的优化。有一次用户抱怨说我的脚本太慢了,仔细打断点统计了,存取其实也才三分钟,重头全都压在了计算。一开始觉得没有任何优化空间,直到有一天我无聊,开始逐行代码分析倒地哪里慢。于是乎发现了以前一直很爱用的方法的惊天大坑。

元素级别迭代与series,ndarray的迭代

前者与后者在运行效率(速度)上有天然的差别。python在ndarray和series整体运算远远快于元素逐一的运算。因此我们应该多利用这个特点去避开针对元素的判断和计算。
比如:在做固定资产折旧,上一月末NBV-当月折旧=当月NBV。这种迭代需要通过数据结构的变化来避开元素判断。
方法1:

如图,我们按照这种数据结构计算,避免不了在行上,每一条资产,元素级别按照判断P几,然后再计算NBV和Dep,再shift,那可想而知速度很慢。不过节省空间。代码如下:

##此算法为迭代计算,即前一个运算的结果为后一个运算的参数。eg: a=b-c  --> b=a --> a2=b-c-c
def iteration_cal(dataframe, new_col, Period_col, Begining_col, cal_col, num_of_period=None, Begining_Period=None):df = dataframedf[str(new_col)] = 0iter_count = int(num_of_period - Begining_Period + 1)count = 0for i in range(0, iter_count):if count < iter_count:df[str(new_col)] = df.apply(lambda x: float(x[str(Begining_col)]) - float(x[str(cal_col)])if x[str(Period_col)][1:] == str(Begining_Period)else (df.loc[x.name - 1, str(new_col)] - x[str(cal_col)]if (x.name + 1) % num_of_period > Begining_Period or (x.name + 1) % num_of_period == 0else 'Pending'), axis=1)count += 1return df

方法2:

我们不如换一种数据结构,如图。这个方式是之前写的小文章,stack结构的应用。一切迭代都是针对列的运算(series or ndarray)。只要你没有那么多的Period(成百上千)(不然会内存爆掉),一般足够了。代码如下:
列计算的代码:
由于用户订的需求逻辑很多,下面挑选了个简单的例子。

##NBV_1单独先算出来。。。。
for i in range(1, 49):dep_calc_df['Depr_'+str(i+1)] = dep_calc_df.apply(lambda x: x['NBV_'+str(i)] if x['NBV_'+str(i)]<x['Depr_'+str(i+1)] == 1 else x['Depr_' + str(i+1)], axis=1)

后面stack结构转换参考之前的文章,当然你也可以选择pd.melt/pd.pivot兄弟。
而今天谈到的坑,就是在列计算时候遇到的。'axis=1’其实就是逐行计算,而且还有判断的逻辑,60万行的情况,差不多这一套下来就好几分钟。

测试

我们可以找一组作对比。控制变量,我们用同一组数据,干同一件事:

算法A代码:

start = time.time()
calc_df_test['Col_C'] = calc_df_test.apply(lambda x: x['Col_A']+x['Col_B'], axis=1)
end = time.time()
print('测试耗时:'+str(end-start)+'秒')

算法B代码:

start = time.time()
calc_df_test['Col_C'] = calc_df_test.eval('Col_A+Col_B')
end = time.time()
print('测试耗时:'+str(end-start)+'秒')

算法C代码:

start = time.time()
calc_df_test['Col_C'] = calc_df_test['Col_A'].values + calc_df_test['Col_B'].values
#这个.values要不要无所谓
end = time.time()
print('测试耗时:'+str(end-start)+'秒')

三种算法运算速度如下:

因此我们有一种想法,将所有针对元素级别的遍历判断,都转化为ndarray之间的数学计算,这样时间效率至少提升几十倍。

series, ndarray之间的数学计算,替代元素遍历判断。

那么如何将上述的逻辑判断,转化为纯数学计算呢?
换句话说,我们如何把判断这件事情,做成数学运算?那么,我们先来想一个问题,如何判断位置?对了,位置就是向量坐标对吧。那么我们可以参考这样的思想,去把我们用于判断位置的逻辑,替换成向量坐标。

举两个栗子:

栗子1:单组向量(一层批判断)参与计算。

上图可以看出,其实我们只需要找到那一个需要“修正”的月份,将它改成正确的金额就可以了。换句话也就是锁定那个位置,再去替换,而这一切需要避开axis=1的元素遍历。
我们可以这样:
如下图用制作好的向量组(可以选择用作差的金额 ),乘以作差的数值,再把结果和原始数据相加,这样刚好被锁定的“位置”那个月的数变成了我们判定后想要的数。全程都是运算没有针对元素判断循环。

代码案例如下:全部都是数学运算替代元素级别循环,但是注意“!!”叹号下面的还是用了if, else的判断,之后我们会讲如何用纯数学方法替换它,以及,为什么在这里不做替换。

##单独计算好NBV_1和Depr1的数据
for y in range(1, 52):df_base_output_3['Rem' + str(y + 1)] = df_base_output_3['NBV_M' + str(y)] - df_base_output_3['Depr_M' + str(y + 1)]#!!!!!df_base_output_3['Rem' + str(y + 1)] = df_base_output_3['Rem' + str(y + 1)].apply(lambda x: 0 if x >= 0 else x)df_base_output_3['Depr_M' + str(y + 1)] = df_base_output_3['Depr_M' + str(y + 1)] + df_base_output_3['Rem' + str(y + 1)]

栗子2:多组向量(多层判断)
判断的逻辑越多,我们需要的向量组也越多。比如,下面这条逻辑:判断某一家店的开店时间如果小于当年则为0,等于当年而小于当月则为0,其他情况为两个时间点的相隔月数。

同样的理论,二维数组里找下图红线范围以上的坐标,都满足以上的筛选判断要求。以此类推,那么三位向量空间中则是找满足两点距离的特定坐标。

根据数量级大小,选择适合的算法

刚才说到的:

df_base_output_3['Rem' + str(y + 1)] = df_base_output_3['Rem' + str(y + 1)].apply(lambda x: 0 if x >= 0 else x)

这段代码,我们依然用了判断。但是可以接受,为什么呢?因为他是针对单独series,进行判断,x此时代表series or ndarray,而不是dataframe。不过,我们依然可以选择用纯数学计算来替换掉这层series的判断。

我们来像一件事,这句话的本质就是在制作向量组,输入所有正数和0,返还0;输入负数返还1。其实有很多种方法可以到达这个目的,下面举个例子:

##方法1强烈不推荐,但是稳定性好!
def Num1(num):value = []num=math.floor(num)+2for i in range(2, num + 1):for j in range(2, i):if i % j == 0:breakelse:value.append(i)alist=value+[3]value=alist[0]-2return value
##方法2可以尝试
def Num2(num):# num=math.floor(num)+0.9num=num+0.0000001num=((num/abs(num))-1)/(-2)return num

方法1强烈不推荐,本质意义就是调整输入数字的质因数分解后的第一个数(2或者-2).但是有点稳定性好,不过速度一定很慢。
方法2貌似不错,本质思想就是输入的这个数字,除以它自身的绝对值,要么是1要么是-1,在这基础上减去1则为0或者-2,再除以-2那么就是0或者1.
唯一的顾略在于如果输入0的话,0不能为分母,那么我们调整输入值,让它+0.0000001,这个调整的数字必须保证原始NBV-dep不能出现绝对值比这个数更小的存在。否则可能就错了
这时候看,一般数据库后台都会设置类似decimal(20,6),看好你字段的小数限制.如上述设置,就可以放心的设置调整的参数,不会有超过6位的情况出现.

我们来看一看性能表现吧:
原始判断方法:

df['A']=df['Cost'].apply(lambda x: 0 if x>=0 else 1)

纯数学运算代替判断:

def Num2(num):# num=math.floor(num)+0.9num=num+0.0000001num=((num/abs(num))-1)/(-2)return num
df['A']=df['Cost'].apply(lambda x: Num2(x))

测试结果如下:
不难看出再数据量小的时候,原始判断更优。但是当数据量逐渐变大的时候,纯数学计算的方式更快。当然中受限于你的算法。因此我们可以再实际操作中,去尝试找到特定场景下那个数据量的阈值,从而选择最优的算法。

那么如果还想优化这段算法,换一种数学方式去替换判断的方法,还有别的方法嘛?
肯定有的。比如,我发现对数函数可以用以下哦。 因为log以a为底1的对数为0(a为常数) 恒过点(1,0)。

那么我们代码可以改成如下,似乎更简单:

try:df['A']=df['B'].apply(lambda x: math.log(1,x))
except:ValueError

为什么要try?因为B有可能是负数…这个try还是可以经常用到的。
有时候人觉得复杂的,计算机不一定觉得复杂,相反它可能会认为一层层判断很费脑。这是一个观念的转变。

*补充:感谢周老师~!

上面关于制作向量组那些呼呼轩轩的操作,什么绝对值调整,质因数,对数函数,真的很尬。周老师说他做只有一句话:

(df['A']<0)*1

没错,小于0的为1,其他为0。所以我总要反思一个问题,py是为了减轻工作员的工作量,而不是让你去创造。能有内置就尽量用内置,不知道就去查!py的内置语法底层都是C语言在执行,比我们快的多的多。比如,之前我还总去写那些排序算法,冒泡排序,希尔排序,插入排序。。。。其实py就一句话sorted()快而精准。。。。感慨下!

算法优化---向量数组计算替代元素级别判断相关推荐

  1. 算法----- 在排序数组中查找元素的第一个和最后一个位置

    题目: 在排序数组中查找元素的第一个和最后一个位置给定一个按照升序排列的整数数组 nums,和一个目标值 target.找出给定目标值在数组中的开始位置和结束位置.你的算法时间复杂度必须是 O(log ...

  2. js 数组(定义数组,数组长度,数组计算,元素类型转换)

    一.定义数组的方法: 1. var arr=[]; 2: var arr=new arrary(); 3: //split() 方法用于把一个字符串分割成字符串数组. var s=("1,2 ...

  3. 【潮流计算】基于matlab粒子群算法优化电力系统潮流计算【含Matlab源码 2157期】

    ⛄一.粒子群算法简介 1 标准粒子群优化(PSO)算法 PSO算法根据对环境的适应度将群体中的个体移动到好的区域,将每个个体看作是D维搜索空间中的一个粒子,根据粒子本身的飞行经验和群体中其他同伴的飞行 ...

  4. [算法题]返回数组A的元素组成的小于n的最大数

    最近闲逛论坛,偶然在力扣上看到一个字节面试题.有人记下来发到了论坛里,也许是刚发出来不久,下面回应寥寥. 最近对算法兴趣日增,正好看到这个题,就想把它解决了. 题目如下: 大概要求就是 数组A中提供了 ...

  5. java二分查找算法字符串数组_Java 算法——二分查找数组集合关键元素

    packagecom.sinosoft;import java.util.*;importjava.util.stream.Stream;/***@authorCreated by xushuyi * ...

  6. 【电力系统】基于粒子群算法优化电力系统潮流计算附matlab代码

    ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信.

  7. Java计算素数算法优化以及拓展

    package javaBlog;import java.util.ArrayList; import java.util.List;public class GetprimeNumber {/* * ...

  8. 编程算法 - 将排序数组按绝对值大小排序 代码(java)

    一个含有多个元素的数组,有多种排序方式.它可以升序排列,可以降序排列,也可以像我们以前章节说过的,以波浪形方式排序,现在我们要看到的一种是绝对值排序.对于数组A,绝对值排序满足以下条件:|A[i]| ...

  9. Linux随笔10-Ubuntu网络配置、非交互式远程主机登录以及shell中的数组应用(冒泡排序数组中的元素)

    Contents 1. Ubuntu系统网络配置总结 1.1. 配置主机名 1.2. 配置网卡名称 1.3. 配置网卡IP地址 2. 非交互式远程主机登录 2.1. 使用expect实现 2.2. 使 ...

最新文章

  1. 【JS基础】Array数组的创建与操作方法
  2. 指针增量和数组的关系,指针偏移的补充,(重要面试),gdp调试,将数组中的n个元素逆序存放
  3. Site Definition和Web Template的区别
  4. 关于对接保税仓物流系统或支付系统推送报关单的一些琐碎的问题
  5. When is abap.js loaded by Launchpad
  6. BASH中字符串的处理
  7. Redmi Note10系列发布时间曝光:最高搭载1亿像素主摄
  8. vrrp协议原理与应用
  9. Linux时间子系统之六:高精度定时器(HRTIMER)的原理和实现
  10. CISCO路由器连接ADSL之PPPoE配置
  11. Gartner 如何看 RASP 和 WAF?
  12. paip.Winista HTMLParser文本结点的获取
  13. 简明 python 教程 书_Python简明教程是不是这本书(简明python教程书本)
  14. Performs recursive(递归) glob(全局) with given suffix and rootdir,使用os.walk(rootdir)和filename.endswith(s
  15. light动名词_英语里有些动词有名词形式,那还用不用它的动名词?怎么区分?...
  16. 计算机黑屏然后蓝屏怎么办,突然蓝屏死机开机黑屏怎么办_蓝屏之后重启屏幕黑屏的解决方法...
  17. matlab水印提取
  18. SQL连接MYSQL出现对象名无效_SQL数据库时提示对象名'XXX' 无效
  19. java 指纹比对 算法_Java通过sourceafis比对指纹图片的相似度判断指纹
  20. 一起来看看华为云的裸金属服务器

热门文章

  1. 如何在3GPP网站找到自己需要的技术标准/报告
  2. android短信加密(发送加密短信,解密本地短信)
  3. word中显示删除内容
  4. MySQL ERROR 1017 Can’t find file ‘xxx.frm’…错误的解决方法
  5. 戴尔电脑显示正在锁定计算机怎么办,如何重置Dell计算机上的管理员密码
  6. idea浅色主题及代码字体颜色配置
  7. 有点意思:不患寡而患不均,不患贫而患不安
  8. Xilisoft.Audio.Converter.v6.4.0.20120801.Multilanguage-LAXiTY
  9. 密室逃脱计算机监控,现在的密室逃脱玩得也太大了吧,如果没有安装监控那还得了...
  10. foxbot机器人指令_FOXBOT编程手册.doc