作者:小小明
Faker库生成测试数据
Faker库可以用来生成我们需要的测试数据,安装:
pip install Faker
生成示例:
from faker import Faker
fake = Faker("zh_CN")
fake.profile()
{'job': '驯兽师/助理驯兽师','company': '盟新网络有限公司','ssn': '620922197308261288','residence': '青海省凤兰市花溪杨路u座 985024','current_location': (Decimal('-39.1941315'), Decimal('94.196658')),'blood_group': 'B+','website': ['http://ca.cn/', 'https://shaomao.net/', 'http://68.cn/'],'username': 'chao88','name': '杨坤','sex': 'F','address': '浙江省兴城县西峰叶街C座 376696','mail': 'guiyingfu@gmail.com','birthdate': datetime.date(1951, 2, 3)}
faker 提供的Standard Providers包括:
address | date_time | person | credit_card |
automotive | file | phone_number | currency |
bank | geo | profile | lorem |
barcode | internet | python | misc |
color | isbn | ssn | |
company | job | user_agent |
文档链接:
https://faker.readthedocs.io/en/master/providers.html
于是我们可以通过以下代码批量生成数据:
import numpy as np
import pandas as pd
from faker import Fakerfake = Faker("zh_CN")
size = 1000
person_data = [(fake.name(), fake.address(), fake.phone_number())for _ in range(size)]
df = pd.DataFrame(person_data, columns=['name', 'adress', 'phone'])
df['age'] = np.random.randint(25, 55, size)
df
由于我们只对单列进行迭代,于是只需生成单列的数据,但数据量一定要够大,下面将生成500百万条姓名数据:
import numpy as np
import pandas as pd
from faker import Fakerfake = Faker("zh_CN")
size = 5000000
names = pd.Series([fake.name() for _ in range(size)])
names
经测试生成500万条数据耗时近四分钟。
这对于我来说,感觉还是太慢了,于是我决定换自己写的生成算法试试。
4秒生成500万条随机姓名数据
经过一番尝试写出了如下代码:
def fake_name(size):xing = '赵钱孙李周吴郑王冯陈褚卫蒋沈韩杨朱秦尤许何吕施张孔曹严华金魏陶姜戚谢邹喻柏水窦章云苏潘葛' \'奚范彭郎鲁韦昌马苗凤花方俞任袁柳酆鲍史唐费廉岑薛雷贺倪汤滕殷罗毕郝邬安常乐于时傅皮卞齐康' \'伍余元卜顾孟平黄和穆萧尹姚邵湛汪祁毛禹狄米贝明臧计伏成戴谈宋茅庞熊纪舒屈项祝董梁杜阮蓝闵' \'席季麻强贾路娄危江童颜郭梅盛林刁钟徐邱骆高夏蔡田樊胡凌霍虞万支柯昝管卢莫经房裘缪干解应宗' \'丁宣贲邓郁单杭洪包诸左石崔吉钮龚程嵇邢滑裴陆荣翁荀羊於惠甄曲家封芮羿储靳汲邴糜松井段富巫' \'乌焦巴弓牧隗山谷车侯宓蓬全郗班仰秋仲伊宫宁仇栾暴甘钭厉戎祖武符刘景詹束龙叶幸司韶郜黎蓟薄' \'印宿白怀蒲邰从鄂索咸籍赖卓蔺屠蒙池乔阴鬱胥能苍双闻莘党翟谭贡劳逄姬申扶堵冉宰郦雍卻璩桑桂' \'濮牛寿通边扈燕冀郏浦尚农温别庄晏柴瞿阎充慕连茹习宦艾鱼容向古易慎戈廖庾终暨居衡步都耿满弘' \'匡国文寇广禄阙东欧殳沃利蔚越夔隆师巩厍聂晁勾敖融冷訾辛阚那简饶空曾毋沙乜养鞠须丰巢关蒯相' \'查后荆红游竺权逯盖益桓公万俟司马上官欧阳夏侯诸葛闻人东方赫连皇甫尉迟公羊澹台公冶宗政濮阳' \'淳于单于太叔申屠公孙仲孙轩辕令狐钟离宇文长孙慕容鲜于闾丘司徒司空丌官司寇仉督子车颛孙端木' \'巫马公西漆雕乐正壤驷公良拓跋夹谷宰父谷梁晋楚闫法汝鄢涂钦段干百里东郭南门呼延归海羊舌微生' \'岳帅缑亢况郈有琴梁丘左丘东门西门商牟佘佴伯赏南宫墨哈谯笪年爱阳佟第五言福'ming = '伟刚勇毅俊峰强军平保东文辉力明永健世广志义兴良海山仁波宁贵福生龙元全国胜学祥才发武新利清' \'飞彬富顺信子杰涛昌成康星光天达安岩中茂进林有坚和彪博诚先敬震振壮会思群豪心邦承乐绍功松善' \'厚庆磊民友裕河哲江超浩亮政谦亨奇固之轮翰朗伯宏言若鸣朋斌梁栋维启克伦翔旭鹏泽晨辰士以建家' \'致树炎德行时泰盛秀娟英华慧巧美娜静淑惠珠翠雅芝玉萍红娥玲芬芳燕彩春菊兰凤洁梅琳素云莲真环' \'雪荣爱妹霞香月莺媛艳瑞凡佳嘉琼勤珍贞莉桂娣叶璧璐娅琦晶妍茜秋珊莎锦黛青倩婷姣婉娴瑾颖露瑶' \'怡婵雁蓓纨仪荷丹蓉眉君琴蕊薇菁梦岚苑筠柔竹霭凝晓欢霄枫芸菲寒欣滢伊亚宜可姬舒影荔枝思丽秀' \'飘育馥琦晶妍茜秋珊莎锦黛青倩婷宁蓓纨苑婕馨瑗琰韵融园艺咏卿聪澜纯毓悦昭冰爽琬茗羽希'xing = np.array(list(xing))ming = np.array(list(ming))names = pd.Series(np.random.choice(xing, size))names += np.random.choice(ming, size)names.loc[np.random.choice(names.index, size//2, False)] += np.random.choice(ming, size//2)names.loc[np.random.choice(names.index, size//50, False)] += np.random.choice(ming, size//50)return names
经测试生成500万条性能数据仅耗时4秒钟,比fake的4分钟快了60倍:
%time names = fake_name(5000000)
names
Wall time: 4.08 s
0 汝青
1 缑天
2 喻华
3 丘妍强
4 生香...
4999995 全武
4999996 费舒
4999997 裴婷欢
4999998 葛芸
4999999 燕龙
Length: 5000000, dtype: object
测试Pandas的三种迭代的性能
Pandas内置的迭代器函数有很多,但看一眼就知道性能会很低,因为它们都会给跌带出来的每个元素封装一个Series对象,所以也不在本次测试的范围之内。
本次测试的目的是比较Series对象的str处理器、apply方法以及for循环迭代numpy数组的性能。
测试前,我们先随机取100万行数据加上空格:
t1 = np.random.choice(names.index, 1000000, False)
t2 = np.random.choice(names.index, 1000000, False)
names.loc[t1] += ' '
names.loc[t2] = ' '+names.loc[t2]
下面开始测试:
print("names.str.strip()")
%timeit names.str.strip()names.loc[t1] += ' '
names.loc[t2] = ' '+names.loc[t2]
print("names.apply(str.strip)")
%timeit names.apply(str.strip)names.loc[t1] += ' '
names.loc[t2] = ' '+names.loc[t2]
print("[name.strip() for name in names.values]")
%timeit [name.strip() for name in names.values]names.loc[t1] += ' '
names.loc[t2] = ' '+names.loc[t2]
print("[name.strip() for name in names]")
%timeit [name.strip() for name in names]
结果:
names.str.strip()
2.23 s ± 41.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
names.apply(str.strip)
1.44 s ± 28.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
[name.strip() for name in names.values]
1.1 s ± 20.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
[name.strip() for name in names]
1.5 s ± 23.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
可以看到str处理器的向量模式耗时最久,apply方法次之,列表生成式迭代numpy数组反倒是最快的,for循环直接迭代series对象比apply稍慢一点。
实际编码中,我们也会直接使用for循环,而不使用生成式,所以再单独测试for循环迭代numpy数组的性能:
tmp = names.copy()
%%timeit
result = []
for name in tmp.values:result.append(name.strip())
1.17 s ± 21.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
可以看到虽然比列表生成式慢了点,却依然比apply方法快。
结论
经过以上测试,可以确定for循环迭代numpy数组会比apply方法更快,比for循环直接迭代Series对象会比apply稍慢一点;一直被推荐的str向量化操作反倒速度是最慢的。
速度从快到慢排序:
for循环迭代numpy数组 > apply > for循环迭代Series对象 > str向量化 > for循环迭代Series内置迭代器函数