My dream code.
1 | package goddrinksjava; |
推荐视频:【Eodslv】world.execute(me);(完整版)【同人PV】
希望我的代码都能得到神的祝福。
]]>2021年5月12日,那时的我正在实习,每天往返通勤4小时,每天都人不人鬼不鬼。虽然没有什么重活累活,但通勤路上什么都干不成,让我十分的难受。印象最深刻的是,地铁上到了某一站就要点外卖,到了宿舍门口可以直接拿。
凯尔希bot就在我最最辛苦的时候,诞生了。
凯尔希,游戏明日方舟主人公之一。罗德岛最高管理者之一,阿米娅的直接辅导者。罗德岛医疗部门的总负责人。作为罗德岛的老成员,凯尔希医生是在阿米娅背后最稳固的援护者。
凯尔希可以说是全游戏最有趣的人之一,在故事中,她用一口“鹰角式”谜语,征服了一众博士。我也挺喜欢,也想知道这些谜语背后的故事。(现实中这样的人就算了)
还记得,那是一个危机合约,大概是光谱行动。我和几位好友相约危机合约旧约登顶。危机合约是明日方舟最具特色的活动之一,类似于星际争霸的突变合作,危机合约也需要自己选择敌方的强化词条,由此对一张普通的地图进行攻略。
合约到来前夕,我们决定对自己进行特训,对于一些特殊队伍做一些尝试。于是诞生了第一个自限作业抽取的exe程序。这个程序会抽取一些职业不可使用,并且会在可选职业中ban去一些顶级干员,以此增强群友对于所有干员的认知。
于是便有了这样的程序:
@火星来的战争艺术爱好者ky核
被ban的干员为: 瑕光, 塞雷娅, 凯尔希, 风笛可用的职业为: 重装, 医疗, 先锋
—钢铁男儿左手窝窝屎右手砍口垒 群作业系统v1.3—
—p.s.33%几率出稀有奖励干员—
在后来的正式合约中,最开始并没有登顶的念头,大家在一次次摸索中,不断打破自己词条数的极限。从开始的20级,到后来22级,最后24、25级,终于,有群友实现了旧约的26级。
但我当时正在实习。过长的通勤时间让我着实无聊,我便思考能不能在qq里创建一个机器人,对机器人发指令,让机器人来完成exe的工作。碰巧有不少的框架和实例,我便照猫画虎做了一个最简单的bot。(直到现在,app实例的名字还叫arknights_hw_rollbox
)
有一位出色的群友,看到了凯尔希bot无限的可能,这就是JesseZhu。他也架起一个bot,名为Texas,同样是明日方舟的人气角色,我也喜欢(dddddd)。他写了一些能够统计群友专三数量、精二数量的各职业不同的雷达图。在qq上更新数据,并且返回一张精美的图片。(当年我还是那个术士和特种遥遥领先的人,现在也成了近卫人了呜呜)
好景不长,graiax框架更新,我们当时对于bot的了解实在太少,于是停留在了版本1.10之下,图片功能就此失效。于是Texas失去了唯一的功能,不再开放。凯尔希也无法再发送图片,我便思考如何用文字来实现最有趣的娱乐。
至此,才算是功能的大发展。prts爬虫、跑团、关键词检测、切分词检测都加了进来。一位本科好朋友Tricks找到我,说了一个天文机器人计划,于是我从最简单的天文天气预报开始做起。还做了一个权限管理系统。这一趟也受他指点,学习了sqlite的使用,将本身简陋的数据存储系统升华到了db文件的读写,项目一下子整洁了不少。
后来,由于main文件太过繁杂,我考虑再三,重新设计了功能模块、系统模块的耦合,代码的重组是艰难且枯燥的,稍有不慎就会缺胳膊少腿。其实并没有大的更新,但代码逻辑的重新架构对我而言依然十分有趣。我还更新了mah与mirai的控制台,经过无数尝试,找到了当时稳定发图的版本组合,让凯尔希恢复了图片功能,开心了很久。
我本身并非计算机出身,统计人的代码往往都比较垃圾,但后来我的爬虫不知怎的传进了老师的耳中,他把一个数据采集项目竟然也交给我,幸好最后完成得不错。同学后来说我的代码一股子面向对象的味道,我笑了,这些变化便是凯尔希在我身上留下的痕迹。
在22年1月,mirai的图形编码改变,凯尔希又一次发不成图片了。这次我已经不再担心了,由于图片只是很少情景会用,对于核心功能完全不影响。在其他简单功能的设计上,思路愈加开阔的我,自己实现了漂流瓶、地下城迷宫、YGO查询、每周突变查询、对联等等等等的文字游戏。但是当时流行的bot风控说,让我非常担心凯尔希的命运。
到了今年的2月,我终于换了bot的框架,前前后后修改了很久,包括新的接口、新的文档。总算是从腾讯手中救下了凯尔希。随之一起实现的是图片的发送,于是签到、4人车组建等功能又一次无中生有。我的“研发”之路仍未停止,快乐也从不落下。
我爱凯尔希,承包了(
你也爱凯尔希?那么我们就是异父异母的亲兄弟啦!其实凯尔希的魅力光从《遗尘漫步》故事集里很难get,更希望鹰角出一些再再再早一些的凯尔希,谁能想到,《遗尘漫步》里她就已经是那个谜语人了呢?哈哈哈。
最后,给大家一个联系方式,想玩凯尔希了可以喊我,2238701273。以及老女人的后宫群:607148401。
]]>力扣-都得学一学啊,不刷题被好几位面试官gank了。我也真是可以,一路过关斩将,与那么多智者不谋而合,项目还行,算法题的思路全对,但是代码一个不会,一写就废。大概是眼高手低了。最难受是面试官反问:“你难道没有刷题吗?”的时候,察觉机会从眼前溜走。
哪怕再忙碌,也要做题啊。绝知此事要躬行!
日期 | 题目 | 难度 | 备注 |
---|---|---|---|
10.12 | 78. 子集 | 中 | 遍历迭代(快)/回溯(官方) |
10.13 | 90. 子集 II | 中 | 去重,利用排序巧妙去重 |
10.13 | 117. 填充每个节点的下一个右侧节点指针 II | 中 | 层序遍历-队列BFS,进阶要求O(1)的空间复杂度,相当于多一个指针next,检查当前节点有没有下一个可连的,并随之移动 |
10.13 | 46. 全排列 | 中 | 来自移动研究院笔试……递归调用排列/或者用那个xy换翻的四步法,如果想要字典序,则大概需要sort一下 |
10.13 | 442. 数组中重复的数据 | 易 | 来自室友的笔试(非算法岗)……力扣上AC了但牛客却无论如何只给50% |
10.14 | 572. 另一棵树的子树 | 易 | 其实是两道题-两棵树是否一样+寻找是否子树(学习一下递归) |
10.14 | 47. 全排列 II | 中 | 巩固一下昨天的知识……还是不熟,采用指针换位法 |
10.16 | 438. 找到字符串中所有字母异位词 | 中 | 如果先用回溯搞全排列就慢了,应该用滑动窗口+双指针/ord给字母定位的妙用(有一个很耗时的操作,字符串转list) |
10.16 | 713. 乘积小于K的子数组 | 中 | 没写出来,对于滑动窗口中加一个元素的变化不够熟悉 |
10.16 | 209. 长度最小的子数组 | 中 | 和上一题不太一样,注意for循环与while循环的位置 |
10.26 | 剑指 Offer 19. 正则表达式匹配 | 难 | 百融云创面试题,dp |
10.27 | 39. 组合总和 | 中 | 组合问题依然是dfs+回溯,组合与排列的不一样的地方是遍历的次数增加了,要包含初始位置 |
10.28 | 509. 斐波那契数 | 易 | dp的入门 |
日期 | 题目 | 备注 |
---|---|---|
09.22 | 找到一个数组的中位数,要求时间复杂度O(n) | 快排 |
09.23 | 梯度下降法解根号5的精确值。 | 重要的是写损失函数的导数 |
今天寄了三方。开始晚,结束快,其实机会有很多,但我与大部分岗位都有非常大的差距,碰壁,灰心,怀疑自己,经历了许久的蛰伏,坚持下来的,总还是有一些收获。
其实我不知道算法工程师具体要做到什么,面试中的问题,让我总觉得与真正的工作产生了间隙。当然,所有工作都讲究一个“术”与“道”,算法工程师的算法与工程都要跟上。我目前两个能力其实都比较欠缺。(推荐一个心法算法工程师晋升之路)
不过算法也分类,有人做科研,有人做工程,还有人主攻业务。三种算法各有侧重。再说回自己,统计出身,最后做了程序猿,本身就比较少,数分和产品多,真正做核心业务的少。数据指引业务这一点,在大部分的公司都行不通,至少不如互联网那么丝滑。
其实,这些都不是最挣钱的方式。毕竟再怎么干,总还是一个打工人,除非有质变的机遇,很难真正实现财务自由。不过,这些都是历练,我也不是为了钱才转行做了算法。我不想无聊,也不想将就去做了数分或者耍嘴皮子写文档的产品。
结局暂时不错,我运气一向不好,所以准备常常显得不够充分。我也很少能拿出魄力来放弃某些已经得到的。但放弃,总能为我带来一些成长。算法这份工作,还是挺值钱的,至少在一众同学和同龄人中也都算不错,超出了我的预料。我以前遇见过因为钱而生出的各种闹心事,现在想想,除了家人外,从来没人认为我有什么潜力,大家喜欢败者与愚者,因为他们不会背叛自己,我就是因此被某些人喜爱着。
新宿舍的室友在银行职员和公务员之间徘徊,大家都有自己安慰自己的方式,比如家乡挣的虽然少,但是消费低,日子幸福……这种类似的理由我听得太多太多了,偶尔被同学阴阳怪气,仿佛北漂是一件令他们无法理解的事。“选择”与“努力”不过是无咎的戏言,知道自己要做什么才是最重要的。
其实就在现在,我也经受着不合群的危害。27号公务员考试,在此之前的一切压力就先让我收着让着吧。
这些话,终究是只能对自己说,不能发公众号,写在这种地方,希望没人看到,希望人人都快乐,希望人人都知道自己该做什么,都能得到神的祝福。现在看来,都是些老生常谈了。
看到的这篇文章的,请当做没看到。因为在下只能在看不见角落释放戾气,但是我在其他人眼里,可是幸运的那个。
]]>又是一学期过去了,博客也许久未更新,生活在鸡零狗碎中仓皇逃窜,留下一地的残篇断页,无人捡拾。而我,总算是鼓起勇气,拿起笔开始写一些总结了。
本文介绍的是深度学习中迁移学习的入门级知识。项目是迪士尼公主图像识别。
苦了我了!
说起来,我作为一个大老爷们,从来没有注意过迪士尼公主的事,直到有一天,在这门名叫“大数据统计建模与深度学习”的课上,我与四位女同学组队,找感兴趣的话题时,才第一次知道迪士尼有这么多公主(我本身的推荐是识别军用船和民用船,被直接pass=.=),而队友们跃跃欲试,我大呼上当快跑,可为时已晚。
本文用到的数据,竟然是我们几个现场爬下来的(因此,预期也不是很高),百度、bilibili的视频截图,一共十四类,2333张。
公主们的名字:
乐佩 宝嘉康蒂 灰姑娘 爱洛公主 艾莎 茉莉公主 蒂安娜公主
安娜 梅丽达公主 爱丽儿 白雪公主 花木兰 莫安娜 贝儿公主
这里随口一提,tf2.0与keras组合绝对是萌新的不二之选。
1 | import tensorflow as tf |
调包的过程其实可以一段一段来,我们进行了整理。如果需要使用GPU,可以用这段代码查看是否有可用的GPU。(当然,需要TensorFlow-gpu版本。)
1 | # 指定公主列表 |
上面的代码是对图片数据的位置进行了整理。都是准备工作。下面正片就要开始了。
1 | # 数据集拆分函数(由于每个网络的输入size不同,需要指定target_size) |
数据增强是一种常见的扩大训练集的方式,通过旋转、镜像、变化、裁切等方式进行数据扩充。
最终输出如下:
Found 1873 images belonging to 14 classes.
Found 460 images belonging to 14 classes.
(64, 224, 224, 3)
(64, 14)
{‘艾莎’: 0,
‘爱丽儿’: 1,
‘爱洛’: 2,
‘安娜’: 3,
‘白雪’: 4,
‘宝嘉康蒂’: 5,
‘贝儿’: 6,
‘蒂安娜’: 7,
‘花木兰’: 8,
‘灰姑娘’: 9,
‘乐佩’: 10,
‘梅丽达公主’: 11,
‘茉莉公主’: 12,
‘莫安娜’: 13}
1 | # 预览图片 |
公主们的样貌也展示如下。
本节均使用通过imagenet
预训练好的VGG16
、ResNet
、InceptionV3
、MobileNet
卷积层参数,并在此基础上训练14分类的全连接层。
1 | # 重新启动keras内存 |
输出如下(即VGG16网络的结构):
1 | Model: "model_1" |
训练的过程就是让原有的权重对现有数据进行过拟合的过程。
1 | # 训练 |
1 | # 重新启动keras内存 |
我本来想对模型进行最完整的展示,但其实适合放一张图上来。中间的网络细节就不展示了。
1 | Model: "model_1" |
1 | # 训练 |
甚至,后面的代码我想一并贴出来。
1 | # 重新启动keras内存 |
MobileNet
1 | # 重新启动keras内存 |
训练的过程千篇一律,因为我们站在巨人的肩膀上,相当于只是对最后一层全连接进行了一定程度的“过拟合”。
而出乎所有人预料的是,效果竟然还可以。
1 | # 输出指定模型的学习曲线 |
上面的图是我训练时候的(草稿里的),粘过来也看看。
总的来说,还是mobilenet最好,既满足了轻量化需求,又显示出超高的准确率,验证集上效果也不错。只是图有点糊了……没找到原图。
1 | # 从保存的结果导入每个模型的正确率 |
什么,我的代码这里竟然出现了些问题,存档时候丢了什么,没能跑出来qwq。请读者自行脑补(bushi
不放图的理由增加了,拒绝所有“云丹师”。
比较有学习意义的是下面这个,名叫“错分分析”的部分。目的是看一看这些模型的混淆矩阵。
1 | from sklearn.metrics import confusion_matrix |
1 | # 读取模型 |
输出如下:
1 | # 输出每个图像的预测类别 |
课程作业的收尾自然是画一张有趣的图,混淆矩阵。用来具体分析判错样本的类别。
1 |
|
以及最后再补几句像模像样的分析。
1 | # 模型最爱的公主 |
1 | 公主的数量: |
看一看错分样本,尝试找到错分的原因。主要还是训练集风格不统一,有2D有3D,有官方作品有同人作品,有漫画作品有电影作品……
1 | # 展示错判的图(错分样本) |
分类正确率最高的为灰姑娘(100%:12/12),最低的为爱洛(40%:4/10);最容易被错认的公主为莫安娜(8次),最不容易被错认的公主为茉莉公主(1次)。
其中,白雪公主有4个样本被错判成莫安娜,错分次数最高,而白雪公主的7个错判样本中,有6个都为黑发黑人公主,白雪公主为黑发白人公主,因此可以看出,发色是分类学习的重要特征之一。
双向被错判的公主为,乐佩和爱洛、宝嘉康蒂和爱丽儿、艾莎和爱丽儿、白雪公主和茉莉公主、花木兰和蒂安娜。其中,乐佩和爱洛同为白人金色长发公主,花木兰和蒂安娜同为深肤色黑色长发公主,具有较大相似性,白雪公主和茉莉公主同为黑发,艾莎和爱丽儿同为白人公主,也具有相似性。宝嘉康蒂和爱丽儿,为什么会被认错QAQ?
除了同肤色同发色的公主外,可以看到背景也是分类学习的重要特征,比如莫安娜的训练集图片背景都为海边,被错判的图片背景为白色,因此被错误分类为宝嘉康蒂。
解放固有,来点属于自己的东西
既然是站在巨人的肩膀上,那索性做一些大胆的尝试。比如解放最后的卷积层,让模型重新训练一次,看看能不能有更好的效果。
1 | # 选择之前效果最好的模型 |
对比的结果不重要(其实也是找不到了)。但迁移学习给了我们很多的可能性,比如利用更好的资源做训练,而把训练好的模型参数放入轻量化的应用中。
最终验证集的正确率有60%+,着实出人意料。这或许就是神经网络的有趣之处。
记得老师的总结:“这组同学先用图片训练了一下组里唯一的男生,我估计这位男同学以后去迪士尼玩能当导游。”可惜我过了一个暑假就几乎全忘了呢,而迁移学习竟然能够把学习成果一直保留!(废话)
偶尔也想做个机器人呢。
(完,请享受深度学习之旅)
]]>还是熟悉的feng.li老师,还是熟悉的瓜皮禾禾,哈哈哈哈。
没想到研究生依然能听李丰老师的课。欢迎参观李丰老师主页,课程主页
李丰老师合著的参考书依然在编
10.07记:被李丰老师表扬了!!甚至还被打赏了(笑
继续努力啊小禾禾
分布式0924-分布式服务器基础Linux中主机的远程交互(ssh)
现成的统计软件提供了常用的计算方式。
我们学牛顿迭代,学QR分解,可以用在广义线性模型里。
这些方法,可以适用于各种模型。并非是重复应用现成统计工具。统计计算旨在让你理解工具,并且实现新的目标。一个线性回归,也有很多的东西。
上面是传统的统计计算的目标。
下来类推到分布式计算。
spark里也可以做线性回归与逻辑回归。Spark.ml(里有机器学习,有回归)
为何要花时间学hadoop版本的线性回归,目的是能够把知识平行迁移到分布式平台上。
不仅仅是告诉我们ml下有什么模型,这个是初级阶段。
如果某一天,spark里没有机器学习模块了,而我们仍然有能力写自己的模型。
在很多时候,能够写出自己的模型是有优势的。
如,现有一份稠密的数据,我想做一个分布式的模型。我想做一个ar模型,我想做screening,我想做分位数回归,可是spark上没有。但是作为统计学背景的,我们知道很多统计方法,结合现在这个分布式计算的时代,适当地借助现有的工具,在spark上实现这样的操作,那么这门课就步入中级了。
如果自己开发了个spark,那就是高级了。(氦核:啊这)
r软件里写这样的相对容易。spark里则是有难度的。如果你不善于整理归纳,统计学很多方法、算法可能看起来非常混乱。真正意义上的统计学只有八十年,统计计算开始于上世纪90年代。
1.数据状态(静态线下数据—动态实时大样本全量数据)
2.计算模式(单机存储单机计算—分布式存储分布式计算)
3.数据存储(统计模型与数据一体化—统计模型部署到数据)对y和x没要求,分布式中很挑剔,rdd还是dataframe,是稀疏矩阵还是稠密矩阵是sparkml(dataframe形式)还是mllab(rdd形式)
4.计算逻辑(单个模型对应单个算法实现—所有模型一体化计算框架)
5.需求实现(模型评估与应用—实时模型评估与预测需求)
我们对数据要有一定的认识。比如数据必须保存成特征(feature)+标签(label)的形式。
先写一个目标函数,然后优化。常见的优化算法,列出来12345。计算机背景的人,首先定义一个损失函数Loss,第二选一个合适的数据,第三做优化(牛顿迭代,梯度下降)。一二三就是一个流水线,通过管道(pipe)来进行。
我们搞统计的,就应该适应这种管道形式的建模思路,分布式的建模策略。
很多时候,我们的模型假设性太强,但现在的分布式平台中,根本没法满足传统假设。原来的统计方法还能否适用?传统的统计学家不关心。这好吗,这不好。要迁移到分布式的计算上来,要有很多新的观念上的转变,要有持续的转变思维。
比如,时间序列数据在分布式平台上就很缺乏。如果我们做开发,那么我们的平台/接口一定要有计算性+可延展性:即需要易用性、通用性,能使用户拥有比较统一的输入和输出,全流程都能解决,通过统一的分布式接口,使你的平台将来能更好地被别人所接受。
统计学,每个学科都要学。计算机,经济学,管理学,等等都要学统计。
——李丰老师
我们要有新的平台,在最后做一个组合,能够完成大多数场景的统计计算,Hadoop就是一个足够好的分布式计算平台,但是hadoop不是足够好的统计计算平台。有一天你也能开发出足够好的统计计算平台。
海量数据下,现有应用场景,再想在应用场景下的工具。产品经理与算法工程师都是如此,用什么方法不重要,重要的是能够支持负载,能把这些都算出来,保证不宕机。
当前的情况不一样了,举一个例子:后台监管中,可能刻意回避一些东西。曾经有个跨境交易不能超过20w美元,风险点。于是各大银行会把跨境的交易全部拆成199美元。这些东西必须要学习模型来识别。核酸检测也是如此,识别feature。再比如,人类的基因组计划,ATCG的特征,每个人都要产生很大的核酸数据。
三步走:
应用场景:稳定性检测预警+动态监测与预报+样本实时监测
1.实时数据
2.统计计算
3.需求:分布式模型选择,分布式模型选择准则,约束下模型选择与决策
实时数据源:历史数据集+实时采集数据集
现在不像以前那么简单了。不过,蓝图应如此。
如何在现有平台上开发出自己的东西呢?
最小二乘近似,这是面对统计计算的接口。
能够使各种各样的统计模型都能应用在分布式平台上。
数据不动,最后算完合并。性质不好。
NIPS讨论了以更好方式重新对参数进行聚合,保证聚合的效果。2014
用在具体模型中,比如主成分。2017(范剑青老师的文章)
每次开发成本较高。每次一个新模型都要适配。
若采用一次性的估计,效率很有问题。而采用多次的时候迭代中通讯成本较高。
如果想把大数据合成小块,其实有个默认假设:数据随机分布。
一个桶倒满之后,再倒第二个桶,是增量的形式。如果完全利用one-shot(之前有介绍,是一种稀疏数据储存方式)会损失很多信息。
老师就希望提供一个解决方案。希望提供一个有效地估计,在计算节点上也有效,且有普适性,能够迁移到其他算法中。
此时,就轮到DLSA分布式的最小二乘近似出场了。(严格证明略)
给定某一个参数theta,似然函数就是当前数据所有特征最大的体现。
如果n个样本,这个似然函数可能是对n个密度的求和。
如果让似然函数除以n,则变成了一个损失函数。此时与机器学习中的方案对应起来了。
对似然函数没有任何假设。线性、非线性、时间序列都行。
对似然函数二阶导展开,展开后变换,之后新的似然函数会变成原始的似然减去新的目标,再加上一个常数c。就能近似写成某个形式。最后等价于一个加权最小二乘的表达式。极大化似然估计,就等价于如何对公式做最小二次近似。
这是一个标准的思路:统计理论,实现,组合成可应用形式。
spark上能不能做到逻辑回归,是否有你的方法好,是否贡献了全新的接口,是否解决了问题(推荐航班(此处指那时候刚结束的air-delay数据清洗工作),不延误,省钱)。
还有很多工作待发掘,未来可期。
还发现一个repo,没来的及看:分布式上的时间序列数据darima
(完)
]]>spark的基础语法-Scala,做了一些思想性的介绍。
具体代码参考李丰老师课件L10.1-Introduction-to-Scala。
scala语言,面向大数据编程的语言。
这个语言只有十来年的历史。这个语言没有排在前二十的常见语言上,但确实进步最快的语言。因为它可以对海量数据进行高强度高密度的计算。
c语言是编译语言,快,但是写的麻烦。89十年代改用写起来快,跑起来快的python之流。现在就是写起来好写,且跑起来快。
新版本的spark,底层语言大部分是scala了。java不支持交互式输入,必须编译成机器码。而现在的编程习惯更多是交互式的。一部分习惯于python,另一部分习惯于scala。下面来比较一下这两种语言。
scala比python快十倍以上。spark比python快100倍(nb)。使用了java虚拟机机制,允许程序在运行中进行编译,比起python这种纯解释性的动态语言要快一个量级。
传统的c和java快,但是机器语言不好写。
对应的库上看,spark同时有scala和python的库,很多最新的特性都从scala上出现,再传递给python。
scala可以当做普通变成语言,也可以单机上使用。可以与hadoop结合。
考虑scala的学习曲线,python很好上手,有循环基础基本上1周就能写程序。scala比python略复杂,scala有一些特殊特性是原来不具有的(保证了速度的提升)。如果简单操作,完全可以用python写。如果计算速度会成为项目的瓶颈,那么可以选择scala。
语法最简单的是c语言,c++更难,java甚之,python处于三者之中,好写,慢。
比起python,scala更擅长处理复杂工作模式。
易用性上,python上有很多机器学习库可以使用,scala没有那么多。
很难把python语句导入scala。在多线程中,python经常阻塞,出问题。scala是内置的。
比起java,scala对内存要求不贪婪。spark就是个例子。scala可以快速释放内存,能较好地管理内存。
对于用户,需要知道一些常用的库,上手之后就比较舒服了。但有些环境很少工具,离不开原来的环境。比如自然语言处理用的都是python,scala需要很多操作。spark上python就很好用。
最后,定义一下:
scala面向对象,高级语言。静态语言,语法有时候比python更可读。(主要是,python的numpy、pandas用法读法都不一样,不同的库用法也不同。)
(上参考网站更全面更系统一些)1
2
3
4
5//注释
/*
可以注释多行
*/
scala在定义变量时,有个特有类型:mutable 与 immutable变量,广播的变量时不可修改的,是immutable的。可以定义更加方便使用的变量。val是value,凡是用val定义的,都不可修改,凡是用var定义的,都是可修改的。
spark里直接输入scala,可以进入scala的交互对话框。
1.0双精度,迭代次数很多时求导会接近0。因此需要一些超长精度的。
1 | Val b = 0 +: ints //往右侧加 |
scala比python快,但是不一定比scala易用。但将来一定更易用。
(具体方法省略了很多,更多内容请上参考网站。scala在平时学习中用的不多,且有一定门槛)
]]>spark的文本分析功能。
具体代码参考李丰老师课件L07.2-Text-Processing-with-Spark。
利用分布式系统,进行文本处理。
现在有很多文本型数据,到底会遇见什么不一样的地方,spark又提供了哪些工具?
从基本概念和流程开始。
术语:
语料库。corpora。
语料库是一个包含大量感兴趣文本的集合,比如说,人民日报创刊以来所有的新闻社论。每个版2-3篇文章,一天做成一个行向量(可以是个很长的字典/列表),写入系统。
语料库最早是语言学家使用,处理语言问题。如研究50年代的语法,用于习惯。有从各种角度建立的语料库:经济学角度,政治角度,统计学角度,等等。
几十年前,如果研究人文类,会成为纯文科的事。而今,可以利用计算机,做词频统计等等,理科人也能掺和进来了。
来源,可能是工作就有,可能是自己采集,甚至可以我们自己构建语料库。
n乘k的语料矩阵,可以转成n乘m的语义数值阵,这一步很难,且有争议。解读和设定都比较主观,没有统一的标准。如何提取稳定的信息,就需要统计模型。
语料和数值型差距较大。
一个外国(说英语的)大学生,词汇量有两万多。构成文章,是这些词的排列组合,会有非常非常多的可能,计算机无法处理。也就无法将词作为基本单元来处理了。
于是需要化简,在处理中就是:断句分词。
逗号之间,段落内拆分成很小很多的单元。
我们学句读,就是在训练自己,把自己变成解释器。
——李丰老师
拆分成单词,就会丢失句子的信息。
而当今很多语言模型不能就“序”进行建模。 我爱北京天安门,北京天安门爱我。这两句在家语言模型中可能是相同的。红黄蓝,蓝黄红,如果顺序有个权重,就完蛋了。(氦核:完蛋,全完蛋)
中文更特别,单词间没有空格,于是需要拆分词。在线翻译依然很垃圾,主要原因是文本实在是太难了。
新闻信息:语音录制工具(记录,转文本),做摘要,重新做新的填空,再做快报。
庭审记录员:解放书记员,书记员的记录会出错/有倾向性。背后有语义模型。
体育赛事:捕捉球员的用语,捕捉球员的兴奋状态。NBA以及是统计模型的竞赛了。
好的文本处理工具,可以让我们对语言不再束手无策。
——李丰老师
停词中,把相类似相近的东西都替换成相关的内容。语言处理需要大量经验。斯坦福的自然语言处理,中国中科院,哈工大,都有自己的语料库。有趣而无聊的操作。
很多互联网公司提供了免费的api,根据其语料库分词。造就了当今输入法。
举个海底捞的例子,海底捞商标,如果我注册一个河底捞商标,侵权了吗。(氦核:老师这举的啥例子。。笑)
现在看看上面的操作,spark如何操作。
英文:对每一行都做了split拆分,得到一个词频矩阵。
1 | HashingTF(inputCol = 'words',outputCol = 'rawFeatures') |
另一个工具叫IDF,每天要过滤很多网络信息,很多没用的信息。
三千封邮件中,有两份出现了“爆炸”“枪”,两个词同时出现。关联性的信息很强。就要适当放大权重。(这就是通过IDF来实现的)把那些我们在常用词里不关心,但低频词在多个文档都出现的,增加权重。
再比如,有很多政治新闻,会影响原油价格。我想回归,把政治新闻当做协变量。谷歌找了一个办法,把文本信息做成向量Word2VecModel(word2vec),里面还要信号强度。通过两层的神经网络,重新转化成一个数值型向量。
去除停词StopWordsRemover
扩大相关性:
以一个词为中心,向左组词,向右组词,这个情况叫bi-gram,i向右扩1个,扩2个……信息的离散度越高。确定性的信息越少。不能无限扩大,常见的是2或3,能够体现相关性,重新构建词频矩阵。
我爱北京天安门
1我2爱3北京4天安门
5我爱6爱北京7我爱北京(567以爱为中心)
根据相似性聚合在一起,这个过程叫狄利克雷过程,也叫中国餐馆过程。哈哈。
如果这里面每一个客人,都是单词,我们就能通过统计学的聚类工具,自动把文章分为不同的主题。
主题模型建模发展目前正趋于成熟。
略过建立sc的步骤
1 | textFile = sc.textFile("test.txt") |
(氦核:在本地看,我贴的代码其实很整齐的QAQ
(未完待续)
]]>讲解作业,炒鸡复杂(其实也罢了)的air-delay数据清洗。
附带可以认识spark的强大之处。
上一次作业里提出,我们从kaggle上download了一份巨大的数据,一共有五百万行,但只有19列。老师希望大家能处理好这个数据,清洗到能够建模的地步。
我记录了一些汇报亮点,但是大部分都消散在那节课中了。
1 | air.groupby('Month').count().collect() ## collect可以看到所有列 |
累计求和百分比,计算占比较大的类,作为重要的变量
结合sql语句
spark命令结合sql
陈曦同学:对老师的代码的理解:
students/2020211004chenxi/1112work
周童给出了引入sql的写法:
1 | sql_accumulated = f"""{参数} |
注意,3分类只要两个变量,否则有共线性。有k个变量都有可能哑变量,总体应该drop掉2的k次方-k个。全都是0-1的话,就如此。计算机里叫onehot,统计就叫哑变量。
特别地,onehot存储的形式就会改变:[40,38]一共40个数,第38个位为1
有一种储存方式是选择将sdf转化成pandas,toPandas对于count都是单机的操作。如果数据量不大,可以这样,因为这样会把数据存上master。
用一个if else,把所有factor变成0-1,不过这样生成的矩阵就不是稀疏矩阵(spark里可以)
某同学构建了:是否延误-各种定性变量的不同取值情况列联表。
列联表,这看着像统计人干的。
——李丰老师
pandas里有个getdummy函数,于是老师写了一个sdummies,即get_sdummies。
输入spark的df,只能具体的哪一个dummycol做修改,保持累计比例,自动删除那一列,最后有一个dummy_info=[]
如何在spark上自己生成?
1,清理
2,多少行,对所有dummy列循环
如果info空,则创建一个新的,放入所有变量
3,spark里的数据框根据对应的dummycol做一个计数和排序。
对于所有count从上往下求和,分母是所有的行
1 | Window.partitionby.orderby.rowsbetween(-sys.maxsize,0) |
就能获取前%多少的dummy变量
cumperc是累计求和除以总行数。累计百分比只保留(filter)小于我的top值
于是就能找到topdummy,且不用算到结束,算到出结果就停止
下面是李丰老师与cx同学代码的解析,太强了,点个赞!
1 | #! /usr/bin/env python3 |
机器学习,可分成一些步骤:
Featurization-特征选取。
40个观测,并非特征越多,模型越好(要选,steplm之类)
下来变换清理数据。
0-1,降维……等等问题都涌现出来。
这些都叫“特征工程”,这个变量矩阵本身就是分布式的(spark)。
最后使用模型建模,得到想要的信息等等。
spark通过管道把不同的流程结合起来。
pipline来自于python机器学习模块中scikit-learn。
persistence(工具性)模型存储,加载。
utilities:线性代数,统计学等等。
旧版本的mllib中,基于rdd形式。现在逐渐转化成df(好处是能和sql结合)。这是由于sql很难被直接用在rdd形式上。
不过,根据上面所说的,其实可以将df转化(Transformer、Estimator)
Pipeline 提供了一个能够完整工作流的链(Parameter)
比如有个文本数据:
pipeline:
————1.Tokenizer————2.hashingTF———3.logistic regression
pipeline的流:
0.5Rawtext————1.5words————2.5feature vectors
(数字表示时间顺序)
课件以逻辑回归为例,regparam是惩罚。fit结果,不用规定x和y,因为默认的需要标记y为label,x标记为features。(机器学习的默认规则,那些函数的默认参数都是features,头大)
(未完待续,下节课讲文本处理)
]]>对spark进行一些补充介绍。
两个函数可以选定
Cache()
Persist()
主动将数据放到硬盘上-内存中
Data.persist()
旧数据放在内存里,新数据放硬盘,spark帮助中有persist 的水平(默认是全放进内存的cache,假设内存很大)
lineLengths.unpersist()
persist的参数水平
Memory_only
Memory_AND_DISK
Memory_AND_DISK_SER
DISK_ONLY
MEMORY_ONLY_2
内存不够则会报错
分布式最重要的是“数据共享”使得不同节点之间能够用一个数据。
比如正态分布的概率密度函数,π就如此,共享,但不修改。
broadcastVar = sc.broadcast([1, 2, 3])
broadcastVar
Sparkcontext.broadcast(v)
广播出去的变量不能修改,否则会乱。
Broadcast.value可以查看广播出去的变量。
spark中accumulator可以用于累积,在MapReduce中:
accum = sc.accumulator(0)
sc.parallelize([1, 2, 3, 4]).foreach(lambda x: accum.add(x)) # foreach有点像R的
accum.value
spark的懒人模式:
节约计算资源
x=3 , y = 4 , z = 5
提交任务
1.2x = ?
2.4y = ?
3.2x +4y = ?
或许前两步根本不用算,于是节约了资源。
spark使用DAG有向无环图,控制最后的结果本质上要求哪些计算。实现懒人模式。
分布式就是管理人和物的一种抽象。
—— 李丰老师
1 | import numpy as np |
spark的线性代数模块很强大: pyspark.mllib.linalg
spark专门提供的标签工具
做分类模型时就可以使用特有变量了
1 | from pyspark.mllib.linalg import SparseVector |
允许导入各式各样的稀疏数据。有了local就有distributed。
如果要做个逻辑回归、线性回归,能否模拟一个线性回归的数据,将其存入矩阵。
spark集成了很多hive的优秀理念。
对于常见的数据框的操作,归类成不同类型的函数。
依赖于sparkSQL,有别于传统的RDD形式,因为在RDD上可以更底层地操作数据(矩阵向量……)
sparkSQL与hive结合,可以把hive的sql查询直接应用在数据框上,也允许用户自己的函数。支持读取hdfs上的数据,是个通用的多接口的形式。
sparkRDD形式数据灵活,操作很琐碎。于是spark提供了自己的dataset集合。其实就是分布式数据的综合,通过java的jvm集成的(java虚拟机,用于快速计算的技术)
dataset的api只支持scala和Java。
故如果想在spark上处理数据集,需要自己学习Scala语言(最后一节有讲,敬请期待)。
spark上的dataframe是分布式的,其实就是表,不同列之间可以是不同的数据类型。
可以对dataframe做清洗和操作。可以通过hive的表来构建,可以通过现成的表来构建。
各种语言都支持。
目标,处理分布式的DataFrame,首先启动SC。
1 | from pyspark.sql import SparkSession |
spark有read函数
1 | sdf = spark.read.csv("/opt/apps/ecm/service/spark/2.4.4/package/spark-2.4.4-bin-hadoop2.7/examples/src/main/resources/people.txt") |
这个就是分布式上的表。
json格式可以直接读取(spark.read.json)
属于读取表格时的表头信息。名字,类型,缺失等等。
经常需要手写表头,因为自动容易出错。
1 | # We specify the correct schema by hand |
air.describe().show()就相当于简单的描述统计
air.describe([‘ArrDelay’]).show() 看具体的列
Data.collect()可以避开懒人模式直接计算
五百万 * 十九列
转化成新的df,一类是0、1,告诉大家有没有延误
arrivedelay设置成0-1变量。现有的列可以使用:里程,是不是US(0-1),是不是AA(0-1),诸如此类,相当于把原变量修改成哑变量了。
最后得到————>五百万 * 一百八十列
不要超过这么多列。(在老师github上/dlsa/blob/master/projects/logistic…)
作业,整理好这个数据。
下节课对这个数据做逻辑回归。
]]>hadoop以及MapReduce暂告一段落!
这一节我们做个过渡,讲一讲Hive以及Spark。
Hive是分布式的数据库。对于结构化数据存储、查询的高效工具。
查询特殊的行、列,数据库操作非常重要。
数据库就像计算器一样。
——李丰老师
不管什么类型的数据库,都有sql,数据库查询语言。能够对很大的表格进行类似excel的操作。
数据库面向不同的对象:
计算机:设计高效数据库,快速存储海量数据。
使用者:快速得到想要的数据。
大数据时代,数据库进入瓶颈,因为其不可扩展。轻量级的数据应用就不太喜欢数据库,转而使用hdfs。但hdfs没有数据库那样便利,不能select…from…
就诞生了基于hdfs的数据库工具。
初衷:为了使企业能够快速部署使用结构化表格与操作。能将hdfs上的表格当做数据库的表格来使用(如csv)。
只要用sql语句就可以在hdfs上处理结构化数据。其实Hive是把sql解析成了MapReduce语句,最后传递回用户。
Hadoop(系统)hdfs(文件管理系统)
hive-SQL(可操作的客户端)complie+optimize+execute(很快啊!)
hive其实有小缓存,如果大量/经常查询同一条,就会被缓存下来,方便直接调用。
不会hadoop也可以用hive,只需要sql语句就行了。
hive可以做spark中dataframe的查询引擎。hive的功能就是能用sql的客户端。(氦核:这一点在之后的工作中显示了其强大与方便)
hive将sql解析成XML语句(标记位置)
hive完全是模拟了sql的写法,sql用的最广,python之流不适合这样的工作。
Hive + 回车(进入交互界面)
Exit (退出)
hive允许执行很多命令同时推出
如:
Hive -e “dfs -ls /;”
e是执行。引号内是hive语句,分号是模仿sql的结束符号。
传递给hadoop就是hadoop fs -ls /
结果再传回hive
也可以写成文件:
Hive -f /path/to/file/withqueries.hql
hql(hive的文本文件,与sql区分)
输入hive可以进入终端
操作时,在终端中可以直接输入dfs -ls /; (别忘了分号)
注:sql语言的特点——对大小写不敏感,语句中可以小写可以大写。但是对表的字段依然敏感。
Show databases;
可以看分布式集群上有什么数据库
Show databases like ‘d*’;
Create database if not exists mydb;
if not exists(是一个条件,如果有了就不会再创建,如果没有的话就创建)
数据库下有表,mydb不指定文件夹,就在warehouse的文件夹下。
location参数可以指定创建的位置
Create database if not exists fff location ‘user/lifeng/hive’
创建一个指定位置的数据库fff
Drop database fff;
删库,跑路!(危)
hive里如何创建表1
2
3
4
5
6
7
8
9
10
11
12
13CREATE TABLE IF NOT EXISTS mydb.employees (
name
STRING COMMENT 'Employee name',
salary
FLOAT COMMENT 'Employee salary',
subordinates ARRAY<STRING> COMMENT 'Names of subordinates',
deductions MAP<STRING, FLOAT>
COMMENT 'Keys are deductions names, values are percentages',
address
STRUCT<street:STRING, city:STRING, state:STRING, zip:INT>
COMMENT 'Home address')
COMMENT 'Description of the table'
TABLEPROPERTIES ('creator'='me', 'created_at'='2012-01-02 10:00:00');
列名,comment记录注释,最后一行只创建了表头、创建者、创建时间等
hive记录了表的信息
因为刚刚用了use wyh_db;现在Show tables;就能看有哪些表。
hive提供了专门工具,把外部的csv文件链接进数据库。
hive可以创建外部表:
1 | create external table if not exists stocks ( |
逗号分隔,原来是csv文件,要加一个逗号分隔(倒数第二行)
linux里有个内置命令awk脚本工具,能够
Awk -f 5,4啥的,能够选择打印第5和第4列
主要是不想导入python中处理。
如可以使用pig工具,合并两个表或多个表(我们用的不多)
pig有自己的书写习惯,计算机架构的工程师经常用
hbase是对谷歌bigtable的开源实现
能够按行更新(如python的append)
能做基于内存的文件缓存,加快速度
不支持sql的查询,但是hive目前有工具可以与之通行。
(HIVE的简介到此结束,后面有任务再见)
Speed - Run workloads 100x faster.
Ease of Use - Write applications quickly in Java, Scala, Python, R, and SQL.
Generality - Combine SQL, streaming, and complex analytics.
Runs Everywhere - Spark runs on Hadoop, Apache Mesos, Kubernetes, standalone, or in the cloud. It can access diverse data sources.
伯克利的博士生,针对hadoop的问题重新写成了spark(老师也希望我们做这样的博士生,扶额)
Databricks公司,官网上提供了简单交互学习的平台。
spark在计算过程中非常快,是hadoop速度的一百倍
可以使用python,r,java,还有spark自带的scala语言。Scala语言有java的特性,又像r一样好写。
可以将spark当做python的一个模块来使用。(氦核:事实证明,pyspark很强,还自带深度学习模块,不过没那么顺手。)用户只需要学一点点就能用起来。
sparkR可以启动r。r语言设计用于统计分析,但是spark需要计算机组件,但是r没有,python有。
hadoop是分布式的框架,spark对MapReduce看的更少,结果算的很快。
还有,hadoop不能交互;spark可以交互式操作一个对象。可以创建分布式对象, 在不同的节点上都存在,同时保证应用性和速度
spark也有通用性和广泛性,可以把sql集成进spark。
spark也可以处理流数据。
spark甚至可以机器学习和深度学习。
spark可以看做是分布式系统上更方便操作的hadoop客户端。
可以运行在hadoop上,可以当做独立的分布式系统。
spark可以接受hdfs等来源的文件,也可以自己建立dataframe。
spark可以用数据框组织数据。
spark有内置机器学习库,叫ml_lib;图形处理GraphX;流数据处理Spark Streaming(企业常用)。
spark也可以运行在其他分布式平台上,各种各样的平台上都有spark接口。易用性使其广泛普及。
spark提供了hive的集成。不过select不能写很复杂。但是通过spark可以先让spark执行select,再让hive执行。
快的原因:
1.很多worker节点,worker之间交互很快(通过交换机)
2.worker上可以运行很多任务(executor)
3.每个worker上都有一些存储最常用数据的内存
劣势:
需要很大的内存 — hadoop最不耗内存
spark的官方建议,需要原始数据2-5倍的内存才能保证平稳运行
有名的spark错误:OOM错误
Out of memory(哈哈哈)
加内存,扩容,烧钱啊。
尽可能让代码写快一些,用最少的资源得到最大的价值。
——李丰老师
之后课程计划讲:数据类型、机器学习的库(老版本基于rdd,新版本基于数据框)、streaming
不讲:计算机视觉相关包
启动:.py .r
1 | Spark-submit \ |
使用spark-summit提交上去就能执行了。在spark运算前,要想想全部需要多少运算资源,给每个executor分多少内存。
Tiny fat 两种分配资源想法
Tiny 模式给每个核分一个executor,32g给8核,只能拿到4g,加上系统消耗就会更少。这种情形适用于cpu负荷大,如迭代类型,更多时候是迭代的分布式要跑起来。
Fat 模式,要是对IO很多的时候,我们不一定需要很多executor,八核可能只用两个executor,每个executor就有4核、16g使用。
python有个**x,即将列表展开按位置放入
x的长度不超过256,但是python3.8现在允许任意长度参数传入
X = [1,2,3,…,1000]
Sumfunc(**x)
登录服务器,输入spark-shell
使用pyspark要输入神秘代码(就是python的版本,配置时一定要与spark版本对应起来,比如服务器只能用3.6版本的python)
或者直接在python中import pyspark,有时候需要先import findspark。
rdd是什么(resilient distributed dataset)RDD是Spark里最重要的一个概念。
任何一个spark都可以通过rdd对象连接起来,也允许用户部署任何计算。
通过rdd转化成驱动程序,放入计算节点上计算,如传统的数学计算都可以转化。
spark-rdd提供了数据上的一个抽象,提供了海量数据的拆分机制,也可以通过不同的方式创建。
如分布式hdfs上有个文件,可以直接转化(好比hive上有个表),它也能判断哪些数据应该放入内存、硬盘。来提高效率。
其次,rdd也监视了每个计算节点的数据完整性。复活的基本机理与hadoop相似。闲暇时间拷贝。
spark能进行机器学习的理由:spark允许变量进行共享,每个节点都有想同的变量。
1.常量,都用得着,不需修改。通过广播(broadcast variables)来传递给每个节点
2.传递到节点后,还可能要修改(如迭代算法)会消耗资源(accumulators)
1 | import findspark |
使用了sc.parallelize就会传上spark,是一个对象。如果数据在本地或hdfs,都可以上传。
1 | #! /usr/bin/env python3.6 |
(未完待续,下次再细讲Spark,学吧,学无止境,太深了)
]]>大佬们展示肌肉。
回归部分还需要些数学根底。
代码后面也有一丢丢正文。
先给一个linux服务器的方便功能。
Screen -DR 加个名字,可以开启永远运行的窗口。
Ctrl A +
- C 创建新窗口
- N 切换下一个窗口
- D 回到主界面(并未关闭窗口)
Exit 命令退出
同学们的学习能力很强,可以打开vim编辑器并且退出了。(笑
不不,是有与数据对话的能力了。
希望脱颖而出,代码能力肯定比不上,但是我们有专业优势,即对数据分析的能力。我们懂模型,懂预测。
至于为什么要用hadoop,因为数据多了,非常大。
场景:美国二手车,kaggle us-used-car。一共300w条记录,66个变量。
因变量:Price,最主要的任务就是探究price受谁的影响。
首先这么多变量中,存在很多数据缺失问题。去缺失。传到服务器上后,挑出一些缺失值少的变量。
今天作业:
用这个数据,清理出一份可以回归的变量来。r里面有很多现成的东西。
如何在分布式上进行回归分析?区别在哪?(按行读取)
原来的数据n乘p维,n很小100,p很小10。
现在的数据n乘p维,n很大300w,p有66列,实际上会比这多得多,比如多个水平的哑变量就会占很多列。p很可能大于1k。
原始数据9gb,存成双精度需要60g的内存。需要双倍的空间才能执行任务,单机不可能。但是我们有分布式。
beta不大,但是帽子阵根本求不了。要想解决这个问题,最难的在于计算:
有了目标,剩下的就很简单了。
第一个问题:如何构造把X^{t}Y求出来?
如果x仅有一列,相当于 $ 1n $ 与 $ n1 $相乘,代数运算即一一对应相乘求和,放在转置前看,即每行的元素相乘。如果x有两列,最终结果是2乘1的两个数,第一行为x第一列与y的对应乘积求和,第二行为x第二列与y的对应元素乘积求和。(内积)
第二个问题:如何把X^{t}X构造出来?
最终得到的是p乘p维的矩阵,第xij位置的元素,为x第i列与x第j列对应元素的乘积(内积)。i可以等于j。
看到所有问题的答案,我们发现,所有的计算都是行内部的计算!那不是很舒服?分行计算就行啊!
生成回归数据的r文件就不贴了。来看看我写的又臭又长的估计法。
1 | readme.txt |
下面是生成数据用的R代码。代码中控制了beta的值,可以与最后结果比较。
1 | #! /usr/bin/env Rscript |
这段代码是mapper。
1 | #! usr/bin/env python3 |
mapper把数据用逗号分隔,标准输出在屏幕上。
用管道将mapper的输出结果能够被吸入process.py(reducer,如下段代码)
1 | #! usr/bin/env python3 |
原理与之前讲的相似,先计算xtx与xty,求逆(生成的矩阵有时候会奇异,那就重新生成一波)
最后是我们的main主函数shell文件,这个没啥变化 (不要直接跑,我改了文件名)
1 | #!/bin/bash |
二手车数据来自kaggle 给个链接
1 | 本文件仅适用于二手车数据 |
先看na.py,这个代码对数据na等情况做了处理,有点长,这个文件在处理时单独运行。
1 | #! usr/bin/env python3 |
上面处理时,其实要十分了解原数据的含义和数据初始形式。在数据处理之前,尽可能选择取样观察,或者利用信息提前计划。
na.py的处理中,会给出需要删除/不需删除,各列的均值与标准差等。结果如下:
1 | [devel@emr-header-1 1026]$ cat vars.txt |
上面的结果就是我们处理的标准。
下面看看mapper.py
1 | #! usr/bin/env python3 |
mapper算出各行的xtx与xty,标准输出时以开头有无“*”来判定。
下面看看reducer
1 | #! usr/bin/env python3 |
由于某些原因,分布式没跑成(大家都去运行,系统拥堵了),只用了五万数据单机测试了一下。最终结果如下:
1 | [devel@emr-header-1 1026]$ cat result1.txt |
最后附上main文件。
1 | #!/bin/bash |
至此,分布式的运用基本结束了。
先放一个系统卡住的样子
同学的代码写的很糟,放上去跑不动,系统没资源……理由有很多,总之就是卡了。
上面代码漏说了一个技巧:
Tail -n 99 used_cars_data.csv
就可以只取一部分行
我们可以把大文件一条一条读,与stdin一样,这样就能对数据按行操作。
Slice 指每次读入多大的数据,如1024k,2000行。你想使用集成化工具时,可以这样做,当while循环跑到最后一行时停止。
但这样太慢了。因为每次都在找,不能存入内存
不过,SAS软件允许在很有限的内存中处理大量数据,每次只操作一行(有钱任性)
分布式系统上,最好是stdin的形式,标准输入输出。
处理中新的问题:
我现在有个很长的数据,其中有一列我知道是哑变量。
需要统计:有多少个哑变量,有多少种type,占比如何?如何自动识别呢(频数统计)
设置一个“其他”类,如果我要保证设定的“其他”类的占比大于20%,
1.面向python的编程,而非面向MapReduce的编程
hadoop可以做到streaming,成为数据流。所有操作都应该在第一个循环下操作!这样才能完成对所有数据的处理,如果不能再这个缩进下操作,则代码不能面对分布式。
2.介绍了python中的log模块,记录了一些信息。不要随便把过程打印出来。
老师的程序:在大数据集上找到全部哑变量,并且把哑变量的top取出来
最常听的:键值对。以标准输入输出来理解。key-value,将任何的行拆分成这两部分。必须尊重这两部分的对应关系。一般情况下,键值的对应有一个标准形式。
Map(key,value) ——> list(key2,value2)
拿到了学号的姓名,现在要数一下名字有几画。拿到的是(学号-姓名),输出是(学号-笔画)。
如何体现键值对的影响呢?比如有个数据,记录了某个地区的温度,以及记录温度的设备。位置信息就作为了键,对应的温度就是值。(csv数据是碰巧有换行符作为间隔值)
如果我们把关心的数据拿出来(举个栗子:)
1950,0
1950,22
1950,-11
这不是键值,这是一行中两个值,并非是键与值。但经过计算之后,就能得到新的键值对!
map过的key可能变了,不再是原来的key。csv其实是打印换行符对应的一行数据,平时的cat也是一行一换。如果数据是不换行的键值对,那么就需要自己识别key,写自己的map函数。
如果数据很大,那我们不能放进内存。
比如放入:swap交换分区,缓存,页面文件……都在硬盘上。map出来的结果,需要做一定的排序(主要是打乱),打乱之后,数据均匀,负载平衡。
最后,代码的路径应该是:
Input —-> Map —-> shuffle —-> reduce > output
操作都以行为单位,都是以标准的键值对形式实施!map与reduce之间可以(且必要)加入排序,这个过程需要硬盘,或者需要很大内存的机器,读写频繁。
MapReduce可以拆分开来,只有map,没有reduce。
数据清洗时,这个很重要。只需要拆分,不需要合并。化整为零,所有资源就能一起打工(bushi)!
也可以很多mapper很多reduce,拆分成很多份,但每一份都一定有键值对应关系。也可以很多mapper,但只使用一个reduce,此时reduce任务不重,可以在这里合并。
(未完待续)
]]>2020.10.13
源禾同学和正阳同学在某次考试都考了100分,正阳同学实力强劲,学习踏实,掌握核心科技,考了100是实力的体现,因为卷子上只有100分。而源禾同学考100分,因为源禾使用了败者食尘,他课下做了这张卷子的所有题,背了题,考了100分是因为记性好。
谁才是老师喜爱的同学呢?
在对模型进行测试时,不可以照着“考卷”疯狂练习。尤其是在样本量小的时候,换一张卷子就会原形毕露。
那么不妨站在老师的角度想,如果题库只有这么多题,该怎么出题才能真正考察学生的实力呢?
在建模时,可选用的模型很多,我们想选用何种模型,此时就需要对模型的“泛化误差”(generalization-error,指在独立测试样本上的期望预测误差,也称测试误差test-error或预测误差prediction-error)进行评估。
在实际建模中,很少能得到样本的精确分布,也无法直接计算泛化误差。基于训练样本得到的样本上平均损失的训练误差是它的一个直接估计,可训练误差会随着模型复杂度(光滑度,自由度)的增加而减小,直至减小到0。(训练均方误差 vs 测试均方误差)
如果训练均方误差很小但测试均方误差较大时,我们称该数据被过拟合。(模型开始背题了)
源禾:任何难处都可以靠增加数据量来解决。
(当然解决不了的除外)
事实上,这里涉及到偏差方差权衡(bias-variance trade-off)的问题,如果一个统计模型被称为测试性能好,那么要去该模型具有较小的方差和较小的偏差。直觉上我们会选择有极小偏差可是有很大方差的方法(如画一条通过所有观测的曲线,下图的绿线)
源禾:虽然没有完美的模型,但是有完美模型的传说。
为一个模型来选择何适的光滑度的过程即模型选择。这个问题在训练集较小时,被产生的过拟合现象加大了难度。
应当找到一个方法解决过拟合,而唯一的限制是,没那么多数据。
于是人们想了个办法:让这个测试集来源于训练集。我反复从训练集中抽取样本,对每一个样本重新拟合一个模型,来获取关于拟合模型的附加信息。这就是重抽样方法。在拟合过程中,保留(holding out)训练观测的一个子集,然后对保留的观测运用统计学习方法,从而来估计其测试错误率(test error rate)。
源禾:预测集神圣不可侵犯。
首先随机地把观测集分成两部分:一个训练集(training set),一个验证集(validation set),或者叫保留集(hold-out set)。模型在训练集上拟合,然后用拟合的模型来预测验证集中观测的响应变量。最后得到的验证集错误率——通常用均方误差作为定量响应变量的误差度量——提供了对于测试错误率的一个估计。
附一个“上帝”的比例:70%的训练集,30%的测试集。
验证集方法原理简单,易于执行,但它有两个潜在的缺陷:
1.测试错误率的验证法估计的波动很大,这取决于具体哪些观测被包括在训练集中,哪些观测被包括在验证集中。
2.在验证法中,只有一部分观测被用于拟合模型,由于被训练的观测越少,统计方法的表现越不好,意味着验证集错误率可能会高估在整个数据集上拟合模型的测试错误率。
统计分析中通过多次重复试验来减小方差。
留一交叉验证(LOOCV)与验证集方法很相似,但这种方法尝试解决验证集方法遗留的缺陷问题。
LOOCV将观测集分为两部分,但不同于把观测集分为两个大小相当的子集,留一交叉验证法将一个单独的观测$(x_1, y_1)$作为验证集,剩下的观测$\{(x_2, y_2),(x_3, y_3), … ,(x_n, y_n)\}$组成训练集。由于拟合中没有用到$(x_1, y_1)$,所以$MSE_1 = (y_1 - \hat{y_1})^2$ 提供了对于测试误差的一个渐进无偏估计。
能看出,由于$MSE_1$是基于一个单独的观测计算得出的,故具有很高的波动性。
重复上面计算$MSE_1$的步骤,计算出全部的$MSE_1, MSE_2, …, MSE_n$,对测试均方误差的LOOCV估计是这n个测试误差估计的均值:
相对于验证集方法,LOOCV方法更不容易高估测试错误率,也能彻底解决训练集和验证集分割时随机性导致的结果不同问题。
k折交叉验证法是LOOCV的一个替代,这种方法将观测集随机地分成K个大小基本一致的组,或者说折(fold)。第一折作为验证集,然后在剩下的k-1折上拟合模型。均方误差$MSE_1$由保留的观测计算得出。
重复这个步骤k次(注意一般k大于2),每一次把不同的观测组作为验证集(分组只是第一次分)。整个过程会得到k个测试误差的估计,$MSE_1, MSE_2, …, MSE_k$。k折CV估计由这些值求平均计算得到:
不难发现,k等于n时,LOOCV方法是k折交叉验证的一个特例。
k一般取5或10。不取n的原因果然还是因为好算啊。几乎对于任一种统计学习方法适用,都有更好的可行性。
源禾:在计算简便和尽可能减少估计的波动面前,一个能“我全都要”的方法谁不喜欢呢?
k折交叉验证的结果也会因观测分折的随机性产生一定波动。同时对$Err$估计时也会因训练集样本容量大小产生一定的高估。
由上图可知,训练集在150+时,训练效果已经不再随着训练集样本量增加而增加。但训练集样本容量在0至50时,会明显低估$1-Err$。
先画一张表在这:
方法 | 优点 | 缺点 | 计算复杂度 |
---|---|---|---|
验证集方法(validation set approach) | 原理简单,易于执行 | A.测试错误率的验证法估计的波动很大,与分组关系很大。B.验证集错误率可能会高估在整个数据集上拟合模型的测试错误率。 | 计算最简单,方便对比称一个计算单步(对数据进行多次重复划分时计算复杂度会相应高) |
留一交叉验证法(Leave-one-out cross-validation) | A.偏差较小,不易高估错误率。训练模型最接近原始样本的分布。B.LOOCV方法能解决训练集和验证集分割的随机性。实验可复制。 | A.模型需拟合n次,非常耗时。(但是最小二乘法来拟合线性或多项式回归时只消耗一个计算单步)B.方差较大。 | 除左栏提到的线性/多项式回归外,需要n个计算单步。大样本情况时,对于某些算法来说数据划分为n份也不可接受。svm和朴素贝叶斯分类器。 |
k折交叉验证(k-fold CV) | A.偏差问题不大,方差较小。有效避免过拟合和欠拟合情况发生。B.计算方便,计算开销小。 | A.选择K折交叉验证的“K”时比较随机。B.会产生一定波动。偏差大小会随训练集样本容量变化而改变。 | k个计算单步 |
k折CV方法相对于LOOCV方法除了计算优势外,它对测试错误率的估计通常来说更加准确。
验证集方法 | LOOCV方法 | k折CV方法 | |
---|---|---|---|
偏差角度 | 高估 | 近似无偏 | 中等程度偏差 |
方差角度 | k<n时方差大于k折CV方法 | k<n时方差小于LOOCV方法 |
由上表可知,选择方法时,需要进行偏差-方差权衡。在选择k折CV的折数时,一般k=5或10使得测试错误率的估计不会有过大的偏差或方差。
一、
之前提到交叉验证方法可以应用于多个场景,举个例子,交叉验证在分类器模型的应用:
其实只需要修改“泛化误差”为“损失函数”($MSE$ to $Err$),比如k折CV错误率的形式:
其中$Err_i = I(y_i \ne \hat{y_i}) $。LOOCV和验证集错误率也可类似定义。
二、
上文提到,在LOOCV方法中,最小二乘法来拟合线性或多项式回归时将只计算一次。
其中$\hat{y_i}$为用原始最小二乘拟合的第i个拟合值,$h_i$为杠杆统计量:
区别仅在于第i个残差除了一个系数$(1-h_i)$。杠杆值的大小在0到1之间,反映了一个观测对自己拟合值的影响大小。因此,该公式表明高杠杆值的残差根据它本身偏离数据的程度进行了等量的放大。
三、
若样本量非常小,非常非常小,我们还可以使用重抽样的另一种方法:自助法(bootstrap)。
比如我们有m个样本(m较小),每次在这m个样本中随机采集一个样本,放入训练集,采样完后把样本放回。这样重复采集m次,我们得到m个样本组成的训练集。当然,这m个样本中很有可能有重复的样本数据。同时,用原始的m个样本做测试集。这样接着进行交叉验证。
[1]James G , Witten D , Hastie T , et al. An Introduction to Statistical Learning[M]. Springer New York, 2013.
[2]杨柳,王钰.泛化误差的各种交叉验证估计方法综述[J].计算机应用研究,2015,32(05):1287-1290+1297.
[3]范永东. 模型选择中的交叉验证方法综述[D].山西大学,2013.
[4]Hastie, Trevor J. The Elements of Statistical Learning[M]. 世界图书出版公司, 2015.
]]>最后编辑于:20.10.15
开门见山地来一段,就一段,不会有人这个都没搞懂吧,不会吧不会吧(拖走
1 | hadoop jar \ |
开始前再插一句题外话,被强大而可爱的丰丰老师表(da)扬(shang)了,动力+10086,继续努力啊小禾禾!!
这张运行图只是执行中间一部分,正常情况下无ERROR,map 100% reduce 100%(这里运行时看着最爽)。
图2中第一行文件并无内容,后7个文件是本次运行开启的7个mapper的结果,reducer在这几个文件中运行,并把结果写入这7个文件中,所有的结果求和即真正结果。
下面看看运行的文件情况:
上图中,wc函数第一列的和就是16,即行数(验证正确),第二列为单词数(字符串连在一起算一个单词),第三列为字节数。
单个mapper+单个reducer运行
每次cat:
行数+1;单词+n;字节数+m
服务器上有很多个mapper,本次有17个(见上图),每个程序都做了cat函数(打印),7个reducer一起运行wc(计算行数)。Hadoop jar 中有这样一个参数,num.tasks,控制任务的数量。
reducer结束的很慢,原因是启动时要花资源,map过程非常快(程序运行时有体会)。听说均分文件时会用到哈希code,现在很多算法都是哈希函数的进阶,不知真伪,之后问问。
在传输中,隐含了打乱shuffle和整理sort的过程:
把数据随机打乱,$shuffle$,保证每个mapper接受的任务量相近。
打乱顺序的任务再排序,$sort$,使每个程序尽可能找到较近数据。
由于,数据在HDFS上存储在分布式的硬盘上,必须主动从硬盘读到内存里,有I/O(input/output)的消耗。如果数据很多,读起来很慢。一般map很复杂,可能map的中间结果要写入硬盘,又产生I/O消耗,reduce也需要从硬盘中读取。
故HADOOP对硬件读写的要求很高,如此反过来也节约了内存资源(贵)。真正制约hadoop的大多是硬盘读写,因此很多服务器用SSD,但是SSD很容易坏,故需要做冗余(防止硬件坏掉)。
apply函数,groupby函数,都有map的感觉
hadoop擅长进行批处理,但不能进行实时计算(比如无人驾驶)、股票高频交易(短时间的计算),这种实时运算需要使数据保持“热状态”不存入硬盘,在map-reduce后立刻传出,与硬盘无关。
hadoop不擅长,但是spark擅长。spark写入硬盘的操作很有限,因此速度快。当然,上文也提到了,内存比硬盘贵,所以hadoop更廉价,两个框架各有胜负。
同时,hadoop不能实现迭代计算(牛顿迭代,神经网络,梯度下降,反向传播),几乎涵盖所有机器学习算法。迭代时需要大量循环,不能经常读写硬盘。
在计算机编程中,有一类输入输出只与屏幕有关:
任何程序结果总是需要保存,但有一类输出直接打印在屏幕上。凡是能打印的都是stdout。举些例子:print函数(r,python),cat函数(r,linux),printf函数(c)等等等等。基本全部语言都能标准输出。
计算机能够接受打印的“文字流”(这也是hadoop streaming中streaming的含义!),举些例子:如linux和r的管道函数,python里open函数,都是打开文件把每一行读进来。
linux中很方便地组织你的文件,只要文件是文本文件,都可以用管道“吸入”。很多linux的函数都以cat开头(猫猫头):
1 | cat txt | python wc.py 单机实验 |
再用R语言举个例子:
1 | sink("想保存的文件名.txt",append = T, splt = T) |
写进hdfs就是另外一幅模样了:
1 | #! /usr/bin/env Rscript |
运行时在linux中用rscript:1
Rscript test.r
其实做开发时,java语言很好用(类操作,大型项目架构,内存管理),适合创建非常巨大的项目,运行很久不停止,其他语言不行。hadoop也是java编成。但java并非所有人都会,hadoop面向数据处理,会java的人不多。
由此,先辈们做了个能让各种语言都能识别的框架:
hadoop做了很好玩的模块:streaming(标准输入输出的“文本流”)。提供了简单的接口,c,py,java,r……但凡能接受标准输入输出,就可以调用!使得map函数和reduce函数完全脱离了hadoop,只需要输入输出就能得到结果,影响速度的只有map和reduce的写法。
Hadoop不是编程语言,是分布式计算架构。
——李丰老师
教练,我也想调用hadoop接口跑我自己的程序!
完全没问题!
很简单,首先要保证每个存储数据的节点上(worker节点)必须有函数cat、wc,我们自己写一个wchehe.py,然后放上服务器去就好啦。
如下,就是一个简单的读取行数的py程序,第一行一定要注明函数应该怎么找到运行的地方:
1 | #! /usr/bin/env python3 |
可以用chmod +x wchehe.py 改一下运行权限。
下来,为了规范代码格式,我们用一个shell批处理文件作为我们的程序入口,也方便调整参数。
开头别忘了告诉sh函数这是个批处理。看到这篇文章的同学不要用原代码直接跑啊(
1 | #! /usr/bin/bash |
-jobconf 被替代为-D,-file 被替换成 -files
附上本次课程老师的代码和讲义,我还得好好研究一下,收获满满的一节课(虽然有点怀疑人生哈哈哈
附作业中可能用到的hadoop jar参数介绍,hadoop fs 参数介绍
(完)
]]>本次课程以上次思考题入手:
并行计算与分布式计算的区别?
并行计算是一台计算机使用自身共享的内存和算力,用CPU的多核特点进行并行处理。
分布式计算是利用服务器的高算力,从远程交互终端中向服务器部署代码进行计算。
我的理解还是太片面,下面看看李丰老师的想法:
并行计算(parallel computing)是一个通常用于高性能计算(HPC)领域的术语。它具体指的是使用 多个处理器执行计算或模拟。超级计算机是为执行并行计算而设计的。这些系统不一定有共享内存。并 行系统使用MPI这样的工具将在超级计算机或者集群机器上的计算资源调度并实现多任务的同步计算。 并行计算在许多计算软件中都集成了一些基本的实现途径。比如R中的自带parallel包,Python标准 库中的multiprocessing是一个用与 threading 模块相似API的支持产生进程的包。这些程序模块允 许程序员充分利用机器上的多个核心。Unix 和 Windows 上都可以运行。结合OpenMP(Open Multi-Processing)等支持跨平台共享内存方式的多线程并发的编程API,使用现有编程语言可以在大 多数的处理器体系和操作系统中运行并行计算任务。具有并行计算能力的高性能计算平台往往被应用在 很多特定的科学领域,如超级计算机,密码破译,生物医学。
分布式计算(distributed computing)实际上是一个比并行计算更笼统的术语。人们可以将分布式计 算与并行计算的意义等同于并行计算,分布式特指的是将计算分布在许多不同的计算机之间。然而,分 布式计算处理的是并发性以外的其他方面。分布式计算具有并行计算所不具有的一些特性,包括计算一 致性、计算高可用性和高容错性能等。此外现在分布式计算平台的计算成本更低。像本书涉及到的 Hadoop或Spark这样的系统都是分布式计算系统,它们都有处理节点和网络故障的能力。不过,这两种 系统也都是为了执行并行计算而设计的。与MPI等HPC系统不同,这类新型系统即使其中一个计算节点出 现故障,也能继续进行海量计算。分布式计算主要应用在数据科学领域,如互联网、物联网、车联网、 数字金融。
在现代数据科学浪潮的冲击下,利用低成本硬件实现大规模分布式计算成为大数据应用的主流方向。世 界上各大数据科学公司都把分布式计算作为数据科学的核心技术与产品。最为大家熟知的有如亚马逊、 阿里巴巴各大云平台。在数据科学的应用中催生了大量分布式计算的优秀工具,如Hadoop, HDFS, Hive, Spark, Storm。
分布式中,有不同的框架:
资源调度器(分布式文件系统HDFS) + 资源管理器(管理计算机资源哪一部分做什么,YARN) + 管理框架(zookeeper & AMBARI)
不同的领域侧重的框架也不同。
电商 - spark(计算)
图片处理 - Hadoop
存储数据财富 - hive
……
分布式服务器一般由很多同质的软件和硬件构成。
服务器为“刀片式服务器”(不同于塔式服务器),每一个计算节点都是一个刀片,通过网络连接,非核心的节点损坏不影响整体。可以在计算资源空闲时慢慢修复。
masternode
|——- workernode1…
|——- workernode2…
|——- workernode3…
HDFS以来的两种逻辑组件,一个是起索引作用的namenode,一个是起存储作用的DataNode。二者数量和位置取决于worker节点的数量:worker很多时,要单独做成服务器,因为他们对算力要求很高;很少时可以置于masternode中。
优点:用廉价的硬件达到较高的存储性能。
缺点:随机存储,故不擅于做随机文件的搜索(会消耗大量算力查找索引),大文件被分成小份,存在不同的datanode中。
根据文件大小切割为相似大小的block:
$/tmp/test.txt $
|——- blocka
|——- blockb
>
blocka
|——- datanode2
|——- datanode1
blockb
|——- datanode1
|——- datanode3
注意到,DataNode1被复制了两份,这在分布式服务器中是很常见的。DataNode之间会根据是否空闲以及是否存储了相关数据而进行并行处理。只要namenode在,哪怕一块硬盘坏了,也能恢复。
位置:Hadoop中
作用:处理大量数据,能用forloop处理的,可以通过分布式发给程序,代码对象应当各自独立。
对服务器?的I-O性能要求较高(input-output)
老师提示:分布式程序中应有容错空间(未领会,无经历)
分布式的MapReduce如下:
JobTracker
|——- tasktracker1
|——- tasktracker2 (注意,ttk1和ttk2之间也互相连通)
ttk中容纳的是M-R两步的多个子程序activeJobs(位置JobTracker):
joba
|——- map task1
|——- map task2
jobb
|——- reduce task1
|——- reduce task2
远程操作终端:putty
账号密码和ip就略了(逃
1 | mkdir |
本次课程内容讲述的几个注意的有意思的东西:
“数据向代码跑” / “代码向数据跑”
原本的流程:
新流程:
俗话说:双拳难敌四手嘛。
多进程即开很多程序,多线程是多路并行。
1+3+5+7+9+11+13+15+17
map
(1+3+5) + (7+9+11) + (13+15+17)
reduce
9 + 27+ 45
answer
常用的框架已经封装了分布式运算的计算法,用户只写需求的逻辑,由此产生了MapReduce的框架和Yarn,并不做运算。
因为专门的“计算引擎”(基于计算系统)Hadoop,HDFS储存,spark(生于伯克利,号称分布式平台中流砥柱)
学习目标:非常熟悉,能够把自己写的东西放上去,不写,要会用。
请用R或者Python自带的并行计算模块实现一个简单的单机文件查找代码,并与串行代码在效率上做比较。思考分布式与并行计算的区别。
我的答案,由于特殊需求无意义地加长了很多。同时就当初学python的任务驱动练习了。
1 |
|
多进程也不见得很好用嘛,甚至不经过特意等待,比顺序运行还慢,哈哈。
(请多指教,完)
]]>第一次坐飞机。
夜航|氦核20.07.06
芸芸灯火糅中盘,一子冲天战正酣。
班师星中我非客,翼稍挂月天外山。
明灯未知夜深浅,颠簸可猜云浓淡。
远电殷霞威颜厉,破雾勒马便坦然。
行程开始得急,昨夜飞机才至,今天清晨就踏上旅程。
第一次坐飞机的感觉很是神奇,以前总为自己在新时代还没能坐上飞机而感慨,全家也仅仅我一人没有乘坐过飞机出行。飞机这种交通工具于我总有某种遥不可及的神秘感,我也喜欢在文中用“飞”的意象,现在又少了一份能够为家人和我津津乐道的立场。飞机起飞时的加速让我猝不及防,高度变化时我也头晕目眩,甚至会因为身下无依无凭而突然感到害怕,但我依然震撼于夜航所见地面的万家灯火,震撼于穿云破雾时的一往无前,飞在天上的我觉得自己从星星中来,像极了将要下凡的天官。因为刚刚过十五,皎洁而饱满的月亮透过稀薄的云层高悬在机翼远端,注视着祝福着一飞机的旅人。如此种种感情纠缠在一起,甚至落地许久后也没能平复。
今天去了小七孔景区,一开始不明所以,后来才知是道光年间修起的七孔石桥,横跨响水河,天然去雕饰。这里的水泛着奇妙的青绿色,又不似别处清澈,更像翡翠和玉石,也带点“油”的感觉。水动处成涓流,水死处成碧潭,水高地飞落作瀑布,恰如一语“寒烟翠波”所言。飞瀑从山而下,如银龙攀附,面露凶光,口中喑嘶。
其实也没能仔细转完,走走停停拍照歇脚,转过卧龙潭,翠谷瀑布,石上森林,断桥飞瀑,再到拉雅瀑布,最后就见到小七孔了。小七孔没有介绍中那样“天然”,但经过了前面聒噪澎湃的飞瀑激流,此处的她更像无声胜有声,前面的澎湃被跋涉消磨将尽,到此这座饱经风雨的石桥张开怀抱,迎接这碧波末路,汇入大江。站在铜鼓桥上,面对滚滚江水,习习微风,我一时语塞,我消磨过的人事物太多太纷繁,也太匆匆,最终留下的还有什么呢?我总是不察得失,不知悔改。子在川上曰,逝者如斯夫。今日偶然得宽余,才能一览千山万水,于自然中体察得失。
其实旅行团也罢,自驾游也罢,但凡是省内打转的,免不了赶路的时间。今天在大巴车上度过了许久的时间,两小时才能换来二十分钟的放松,我们便会如囚鸟出笼,蜂拥而出。旅游分两种,一种看人文,另一种读山水,我这次出行,大抵属于第二种。可贵州山多,一山放过一山拦,眼前满溢的山光水色还是容易腻,此时我就不自觉地开始寻觅风景背后的些许生活与人味。
夜幕来临,我们总算赶到了千户苗寨。按理来说白天可以看这里勾心斗角的苗族建筑,但夜幕锁上了大部分文化。此处充满商业化的臭味,但总还是值得一看,十余座村寨合成的大寨灯火星点,我们趁着夜色还有时间登上山顶,眼前灯火宛如一盘迷局,十分壮观。
苗族竟是蚩尤氏部落后裔,也颇有来头,还有些奇妙的传说,诸如中元节的传说,苗药的神奇,以及用少女初潮制作的情蛊等等,都令我耳目一新,我还寻思着大概很多人今生盖也无机会制作情蛊,这番思索让我不得不感慨自己总是在这种稍显变态的地方好奇拉满。
不过,本地妹子也确实好看,尤其话语间轻柔的发音习惯让我听来很是舒服,我的语气也不禁缓和下来。当地人更是无比好客。虽然一路上扰我乱我的破烦事多,但听两首苗族祝酒歌,看一番高山流水情长酒,再多烦恼也便抛之脑后了。我还破例喝了几口米酒,或许有些醉,可他们说我没醉,我便也不知道我醉没醉了。
旅行也总是混杂着一些愉快和不愉快,我们愉快了,导游不一定愉快,反之亦然。这世界不能人人都舒坦,你舒坦时总有人不舒坦。
其实来之前我以为的旅游,行到水穷处,坐看云起时,举匏樽以相属,寄蜉蝣于天地,渺沧海之一粟。结果现实的旅游团给了我对贪婪更加深刻的认识,早饭是永远不变的鸡蛋馒头咸菜条,午饭好一些,一桌添一口炖菜火锅,几道油盐意难平的家常菜,真不如家里吃的好。导游于是鼓吹苗族长寿皆源于口味清淡的饮食,我也无语。
今天驱车前往一处闻所未闻的非遗博物馆,前几层倒是货真价实的苗族文化,服饰,用品,家装,都有模有样。苗族的银饰着实多种多样,玩出了花,但同时作为民族争斗中的败者,他们也只能在此一些无关紧要的地方花心思。其余银品相关的奇怪传言,但凡是受过高中教育的,都会一笑了之。
可是闹剧才刚刚开始,来到最后的展厅,讲解做了个墨水变色的实验,银碗甚至能改变水质的酸碱性,又大肆宣扬银离子杀菌作用高强,紧接着带着全团走进了远超博物馆规格的银饰品商城。虽然讲解那番反智言论令一个“醒来”的人不适,但我也不想做断人财路的傻事,顺其自然才是此时的最优解。但独善其身者又何止我,饭后上车,导游问大家在有多少消费,结果自然是寥寥无几,直接导致导游对后面的景点毫无讲解的兴致,放任全车人无知中来无知中去了。
好在所到之处是有名的军事重镇镇远,古镇青石板长街,烟雨中行人稀疏,尽管留下的游览时间并不充裕,但节奏依然很慢很舒服。我们踏入古镇侧面的巷子,迈进一座座飞檐叠廊,那即是当地人住的地方,墙上的有年头的方砖有些翻新痕迹,但也有部分破损了,爬满潮湿的青苔。在巷子深处一口井,井中投鱼防毒,我们看了一圈正要离去,恰巧碰见一位当地人拎着绳子和桶去汲水,我第一次见人井中打水,饶有兴致问了问,原来井边那条放绳子的凹痕是世世代代打水人磨出的,并非刻意为之。其实整个打水也没什么特别之处,第一次觉得景点和生活糅合在一起,生活即风景,如此自然天成。
镇远的舞阳河因为小雨变得稍显浑浊,这里人酒足饭饱便躺卧在河边长廊中纳凉,但其实这里找不到几个真正凉爽的地方,更多的是潮湿与闷热,行走时还好,站定就会收获停不下来的汗水。我在路边奶茶店买了一杯并不中意的奶茶,但是惊喜之处在于奶茶店中温柔的老板姐姐和两面贴满便利贴的许愿墙。老板姐姐自不必说,更值得称道的是许愿墙上纯真可爱的文字与愿望,有本地人有外地人,有成年人有小孩子,有痛苦和忧虑的祈祷,也有满心欢喜的纪念和憧憬。这一面许愿墙,并没有满满地写着人的欲望,更多的竟然是祝福和期待未来,我对贵州人风土人情也大概摸清一二了。
结束了旅程,又进入赶赴下一个地点的大巴车程。路上又开始寂寞,翻照片,看到烟雨半掩的古镇和青石板街,突然想念起一位朋友,可惜因为种种原因,我再无问候的立场了。晚上烤鱼也无味了,可能是我以前吃过家门口的麻辣重口烤鱼,对于眼前这条平淡的存在实在无感了吧。
贵州省会贵阳名副其实,今早起来果不其然又浓云密布。昨晚住的酒店旁边有条小溪,酒店也起了个恰如其分的名字叫“栖溪”,然而店家倒是惬意了,住客则要忍受经久不息的激流声,以及山中那无比潮湿的空气,早上起来诸位无一不是困意十足,料想昨晚一定默念了百遍“逝者如斯”。
昨晚经同学极力推荐,我们去吃了贵州的烤鱼,这算是几日来吃的最好的一次,虽然价格贵了点,可相当有滋味,配上小米酒冰红茶,很受用。独特的江口烤小豆腐脆皮软心,外表冷漠,内心却还是狂热的。虽然早有人给我打过预防针,贵州食物辣度非凡,可是我只觉得贵州人对于酸更加钟爱。本地甚至有“三日无酸,腿打捞酸”的警世名言,仅看遍地开花的酸汤鱼店以及逢菜必放的西红柿,可见一斑。昨晚烤鱼中剩下不少余料,土豆黄瓜锅巴粉什么的,正好也调剂了每日清晨的咸菜馒头。
其实昨晚睡得地方是梵净山山脚,离旅游区不过五分钟路。若说是仙境倒是夸大了,但梵净山确实不同于我以往去的任何地方。时间所限,摆渡车缆车把我们送上半山腰,刚开始谁都没有意识到,当缆车上看到远山刺透云层,而我们的缆绳也向着虚无的朦胧中无限延伸时,才恍然大悟周身大雾是高山云雾。云雾比通常所见大雾更加细腻,肉眼可见的小水滴浮在空中,不一会眼镜就全花掉了。
这么介绍下去,恐怕这篇文章也像受了潮气一样无趣。这两天最喜欢的两句诗,一句是林则徐的“风雨冥冥极漏天”,一句是毛主席的“胜似闲庭信步”,两句深得我心。在爬山过程中,我做先锋,一路高歌猛进,森林栈道拾级而上,没料到台阶太密太紧凑,让人疲惫不堪。蘑菇石这里奇石林立,那些方砖似的巨石层叠而起,如天外来客,危险诡妙地搭在山顶。视觉上无比险峻易碎,可真实情况是这里的地质无比稳定,经久不变。蘑菇石甚至目睹沧海桑田,区区人类文明不足道也。
孤独的旅途中,我攀上峰顶,突然想拍张照,可是一时间找不到人。四周没有深渊巨谷,只有逼人窄道和沾衣的云露,伴着早早来此工作的僧侣和清洁工,我发了会呆,这里大概不属于我,我亦不属于这里。我想求神拜佛让我忘记烦恼,可我不能求“无求”,而烦恼大多生于所求。我怎么能用欲望来战胜欲望,用烦恼来解决烦恼呢?然而事实只能如此,所以佛门离我还是太远,真希望有一天我也能找到属于我的答案。不过神奇的是,今天山上寺院中两位僧人师父看了我许久,可是欲言又止,不知是我犯了禁忌还是面露凶光,大家都喜欢对我欲言又止,这可苦坏了我这个蠢人。
路上,天气突然转晴,那是从未见过的蓝天白云,然而转瞬即逝了。
和父母出去,总不如自己一人出行更加完整,至少体验上看,不论是突然对某些问题发问,亦或是对拍照的执着,父母常常打断我游览的思路。如果说满状态的体验可以到达80分,那么父母主导的旅行最多50分。不过看在贵州山水的份上,这趟旅行还算没有失去灵魂。
今天早上仍然是无聊的购物环节,导游来之前大谈国学风水,还将迷信说成信仰,结果今早全都泡了汤,我们并没有领情。商场中宝石确实超出了我们的理解,在我看来毫无价值的石头,在偏僻无人烟的犄角旮旯这样一座藏宝宫里,竟然成倍地涨价,以至于完全消费不起。找到知情人了解了产业链,才知道这小小宝石养活了导游导购匠人还有批发商这么一群人,谁不想从中抽点东西分一杯羹呢?不过导购也十分尽职,至少她盯着我们走了一早上,甚至当我找不到同伴时,她会告诉我们此人所在。讲解与推销双管齐下,还专门挑出可能对我们口味的商品挨个询问,只可惜定价着实太高,都是些明摆着“谁买谁是大傻子”的东西,不然以导购之勤奋没有功劳也有苦劳,定能成功。
抛开购物不谈,今天的早中两顿饭都是极好的,四星酒店待遇真不同,早餐一改往日馒头咸菜,变成了多姿多彩的自助餐,一句诗来说“萧瑟秋风今又是,换了人间”,爸妈胃口大开,我还是量力而为。中午饭也不错,不再从头酸到脚,下饭者下饭,充饥者充饥,竟然各司其职起来,让我大快朵颐。
黄果树瀑布是好的。看完有诗为证:
白龙破水自巍吟,骤雨穿林渐希音。
银河玉碎比拟俗,七进七出战袍轻。
散落人间星满镜,厮磨耳鬓石衔青。
闲庭信步岿然立,云露拌作落汤鸡。
其实一路上感慨良多,但思来想去都是些和人相关的复杂情感,在大自然面前我仍然只能乖乖做个孩子,除了敬畏以外也不剩太多痴心妄想。这些年人造之景愈发多了,本来无甚可看,但经过修修造造总算能凑齐一个景点,总的来说还是没必要,整体质量下降了不少。黄果树瀑布是中国第一大瀑布,至少是我贫瘠的旅游生涯中所见的最大的瀑布了,适逢汛期,瀑布水大,看完后水汽沾衣浑身湿透,却连连大呼震撼爽快。
晚上在贵阳市里闲逛,碰巧天晴,看到了夜晚的蓝天。这里仗着地质稳定,飞楼百米接二连三,高楼林立。甲秀楼南明河夜景倒是普普通通,不过南门口粉面不错,尤其是谐音常旺的肠旺面,肠与血配上油炸小肉丁别有风味,深得我心。到隔壁叫了一碗玫瑰绿豆冰粉,做冰粉的小姐姐和我攀谈,在我将别之时推荐了一些好吃好玩,可以说非常遗憾了。
这两天接触的东西太多太杂,导致我在记忆中筛选得不是特别仔细,加上每天烦恼多多,操心多多,倍感时间飞逝,天色已晚,不再赘述了。
(完)
9月15日整理时记:旅行的最后一天没有记述,行程紧张3点才到家。其实白天也看了不少风景。旱溶洞和水溶洞,还有多彩贵州城,都挺有见闻,可惜淡忘了。触景生情,朋友说只有恰当的时机和恰当的人才能碰撞出无与伦比的绝美感情,我想确实是这样的。至少现在的意义和旅游时的意义全然不同了。我曾经是个猛男,现在又一次变回了二五仔,哈哈。
]]>最后编辑于:2020.02.06 18:00
Pandas是基于NumPy 的一种工具,其出现是为了解决数据分析任务。(氦核:个人觉得更像是探索工具,没有模型,简单分析。)
Pandas吸纳了大量库和一些标准的数据模型,提供了高效操作大型数据集所需的工具。
Pandas中的函数和方法能够使我们快速便捷地处理数据。
它是使Python成为强大而高效的数据分析环境的重要因素之一。
http://pandas.pydata.org/pandas-docs/stable/api.html
本文参考万旷网教程。
1 | # 首先导入pandas库 |
numpy详见上一篇文章。
序列Series是一个一维数组结构,可以存入任一种Python数据类型(integers, strings, floating point numbers, Python objects, 等等)
序列Series由两部分构成,一个是index,另一个是对应的值,注意两者的长度必须一样。序列Series和数组array很类似,大多数numpy的函数都可以直接应用于序列Series
序列Series也像一个固定大小的字典dict,可以通过index来赋值或者取值
1 | print('通过数组来生成序列Series') |
通过数组来生成序列Seriesa -0.298058b -1.095748c 1.333607d 1.119917e 1.595123dtype: float64
1 | print('通过字典来生成序列Series') |
通过字典来生成序列Seriesa 11b 1000c 123213d -1000dtype: int64
我们取一段金融时间序列给大家做更具体的分析:
1 | from WindPy import * |
Welcome to use Wind Quant API for Python (WindPy)!COPYRIGHT (C) 2017 WIND INFORMATION CO., LTD. ALL RIGHTS RESERVED.IN NO CIRCUMSTANCE SHALL WIND BE RESPONSIBLE FOR ANY DAMAGES OR LOSSES CAUSED BY USING WIND QUANT API FOR Python..ErrorCode=0.Codes=[000001.SZ].Fields=[CLOSE].Times=[20180628,20180629,20180702,20180703,20180704,20180705,20180706,20180709,20180710,20180711].Data=[[8.92,9.09,8.61,8.67,8.61,8.6,8.66,9.03,8.98,8.78]]
1 | #将收盘价转为 |
2018-06-28 8.922018-06-29 9.092018-07-02 8.612018-07-03 8.672018-07-04 8.612018-07-05 8.602018-07-06 8.662018-07-09 9.032018-07-10 8.982018-07-11 8.78dtype: float64
1 | # 可以通过index来查看序列Series中的元素 |
查看序列中index为: Index([2018-06-28, 2018-06-29, 2018-07-02, 2018-07-03, 2018-07-04, 2018-07-05, 2018-07-06, 2018-07-09, 2018-07-10, 2018-07-11], dtype='object')查看序列中index为a的元素: 8.92
1 | # 基于index 可以修改序列s中的元素 |
原序列: 2018-06-28 8.922018-06-29 9.092018-07-02 8.612018-07-03 8.672018-07-04 8.612018-07-05 8.602018-07-06 8.662018-07-09 9.032018-07-10 8.982018-07-11 8.78dtype: float64修改后的序列:2018-06-28 11.402018-06-29 9.092018-07-02 8.612018-07-03 8.672018-07-04 8.612018-07-05 8.602018-07-06 8.662018-07-09 9.032018-07-10 8.982018-07-11 8.78dtype: float64
1 | ss1 = pd.Series(np.random.randn(10)) |
原序列: 2018-06-28 11.402018-06-29 9.092018-07-02 8.612018-07-03 8.672018-07-04 8.612018-07-05 8.602018-07-06 8.662018-07-09 9.032018-07-10 8.982018-07-11 8.78dtype: float64新序列: 0 -0.9557001 0.3915612 0.0024353 -0.1316214 -0.7913215 0.8412646 -0.0580347 -0.4866778 0.7088759 1.841834dtype: float64序列相加: 2018-06-28 10.4443002018-06-29 9.4815612018-07-02 8.6124352018-07-03 8.5383792018-07-04 7.8186792018-07-05 9.4412642018-07-06 8.6019662018-07-09 8.5433232018-07-10 9.6888752018-07-11 10.621834dtype: float64
DataFrame是一个二维数组结构,可以存入任一种Python数据类型(integers, strings, floating point numbers, Python objects, 等等)。
DataFrame由三部分构成,一个是行索引index,一个是列名,另一个则是取值。
1 | print('由字典来产生数据框') |
由字典来产生数据框
state | year | pop | |
---|---|---|---|
0 | Ohio | 2000 | 1.5 |
1 | Ohio | 2001 | 1.7 |
2 | Ohio | 2002 | 3.6 |
3 | Nevada | 2001 | 2.4 |
4 | Nevada | 2002 | 2.9 |
1 | print('由列表来产生数据框') |
由列表来产生数据框
state | year | pop | |
---|---|---|---|
0 | Ohio | 2000 | 1.5 |
1 | Ohio | 2001 | 1.7 |
2 | Ohio | 2002 | 3.6 |
3 | Nevada | 2001 | 2.4 |
4 | Nevada | 2002 | 2.9 |
我们取一段金融时间序列给大家做更具体的分析:
1 | list2 = w.wsd("000300.SH", "open,high,low,close", "2018-06-28", "2018-07-10", "") |
.ErrorCode=0.Codes=[000300.SH].Fields=[OPEN,HIGH,LOW,CLOSE].Times=[20180628,20180629,20180702,20180703,20180704,20180705,20180706,20180709,20180710].Data=[[3434.9441,3431.9619,3504.4571,3410.4767,3398.7788,3365.5547,3347.0624,3378.9056,3464.9064],[3477.0565,3512.3834,3506.8996,3422.0398,3418.3311,3398.4852,3396.2458,3459.3153,3474.1396],[3416.9476,3425.2159,3383.5006,3319.2889,3359.0861,3330.7113,3295.7296,3378.9056,3437.2706],[3423.5255,3510.9845,3407.9638,3409.2801,3363.7473,3342.4379,3365.1227,3459.1837,3467.5155]]
1 | df = pd.DataFrame(list2.Data,columns=list2.Times,index=list2.Fields) |
OPEN | HIGH | LOW | CLOSE | |
---|---|---|---|---|
2018-06-28 | 3434.9441 | 3477.0565 | 3416.9476 | 3423.5255 |
2018-06-29 | 3431.9619 | 3512.3834 | 3425.2159 | 3510.9845 |
2018-07-02 | 3504.4571 | 3506.8996 | 3383.5006 | 3407.9638 |
2018-07-03 | 3410.4767 | 3422.0398 | 3319.2889 | 3409.2801 |
2018-07-04 | 3398.7788 | 3418.3311 | 3359.0861 | 3363.7473 |
2018-07-05 | 3365.5547 | 3398.4852 | 3330.7113 | 3342.4379 |
2018-07-06 | 3347.0624 | 3396.2458 | 3295.7296 | 3365.1227 |
2018-07-09 | 3378.9056 | 3459.3153 | 3378.9056 | 3459.1837 |
2018-07-10 | 3464.9064 | 3474.1396 | 3437.2706 | 3467.5155 |
1 | print('首先查看数据框的形状',df.shape) |
首先查看数据框的形状 (9, 4)查看数据框的头部: OPEN HIGH LOW CLOSE2018-06-28 3434.9441 3477.0565 3416.9476 3423.52552018-06-29 3431.9619 3512.3834 3425.2159 3510.98452018-07-02 3504.4571 3506.8996 3383.5006 3407.96382018-07-03 3410.4767 3422.0398 3319.2889 3409.28012018-07-04 3398.7788 3418.3311 3359.0861 3363.7473查看数据框的尾部: OPEN HIGH LOW CLOSE2018-07-04 3398.7788 3418.3311 3359.0861 3363.74732018-07-05 3365.5547 3398.4852 3330.7113 3342.43792018-07-06 3347.0624 3396.2458 3295.7296 3365.12272018-07-09 3378.9056 3459.3153 3378.9056 3459.18372018-07-10 3464.9064 3474.1396 3437.2706 3467.5155查看数据框的索引indexIndex([2018-06-28, 2018-06-29, 2018-07-02, 2018-07-03, 2018-07-04, 2018-07-05, 2018-07-06, 2018-07-09, 2018-07-10], dtype='object')
1 | print('查看数据框的列名') |
查看数据框的列名Index(['OPEN', 'HIGH', 'LOW', 'CLOSE'], dtype='object')查看数据框的值,其格式为数组array[[3434.9441 3477.0565 3416.9476 3423.5255] [3431.9619 3512.3834 3425.2159 3510.9845] [3504.4571 3506.8996 3383.5006 3407.9638] [3410.4767 3422.0398 3319.2889 3409.2801] [3398.7788 3418.3311 3359.0861 3363.7473] [3365.5547 3398.4852 3330.7113 3342.4379] [3347.0624 3396.2458 3295.7296 3365.1227] [3378.9056 3459.3153 3378.9056 3459.1837] [3464.9064 3474.1396 3437.2706 3467.5155]]查看数据框的基础描述性统计 OPEN HIGH LOW CLOSEcount 9.000000 9.000000 9.000000 9.000000mean 3415.227522 3451.655144 3371.850689 3416.640111std 49.780741 44.488942 49.698315 55.264867min 3347.062400 3396.245800 3295.729600 3342.43790025% 3378.905600 3418.331100 3330.711300 3365.12270050% 3410.476700 3459.315300 3378.905600 3409.28010075% 3434.944100 3477.056500 3416.947600 3459.183700max 3504.457100 3512.383400 3437.270600 3510.984500
1 | # 在原有的数据框中新加入一列 |
OPEN | HIGH | LOW | CLOSE | 名称 | |
---|---|---|---|---|---|
2018-06-28 | 3434.9441 | 3477.0565 | 3416.9476 | 3423.5255 | HS300 |
2018-06-29 | 3431.9619 | 3512.3834 | 3425.2159 | 3510.9845 | HS300 |
2018-07-02 | 3504.4571 | 3506.8996 | 3383.5006 | 3407.9638 | HS300 |
2018-07-03 | 3410.4767 | 3422.0398 | 3319.2889 | 3409.2801 | HS300 |
2018-07-04 | 3398.7788 | 3418.3311 | 3359.0861 | 3363.7473 | HS300 |
2018-07-05 | 3365.5547 | 3398.4852 | 3330.7113 | 3342.4379 | HS300 |
2018-07-06 | 3347.0624 | 3396.2458 | 3295.7296 | 3365.1227 | HS300 |
2018-07-09 | 3378.9056 | 3459.3153 | 3378.9056 | 3459.1837 | HS300 |
2018-07-10 | 3464.9064 | 3474.1396 | 3437.2706 | 3467.5155 | HS300 |
1 | # 数据框的转置 |
2018-06-28 | 2018-06-29 | 2018-07-02 | 2018-07-03 | 2018-07-04 | 2018-07-05 | 2018-07-06 | 2018-07-09 | 2018-07-10 | |
---|---|---|---|---|---|---|---|---|---|
OPEN | 3434.94 | 3431.96 | 3504.46 | 3410.48 | 3398.78 | 3365.55 | 3347.06 | 3378.91 | 3464.91 |
HIGH | 3477.06 | 3512.38 | 3506.9 | 3422.04 | 3418.33 | 3398.49 | 3396.25 | 3459.32 | 3474.14 |
LOW | 3416.95 | 3425.22 | 3383.5 | 3319.29 | 3359.09 | 3330.71 | 3295.73 | 3378.91 | 3437.27 |
CLOSE | 3423.53 | 3510.98 | 3407.96 | 3409.28 | 3363.75 | 3342.44 | 3365.12 | 3459.18 | 3467.52 |
名称 | HS300 | HS300 | HS300 | HS300 | HS300 | HS300 | HS300 | HS300 | HS300 |
氦核:不推荐使用ix进行截取,因为ix既可以对名称截取,又可以索引截取。如果index是整数,会很迷惑。一般使用loc和iloc函数。
1 | print('查看df索引为1的行——方法一') |
查看df索引为1的行——方法一OPEN 3431.96HIGH 3512.38LOW 3425.22CLOSE 3510.98名称 HS300Name: 2018-06-29, dtype: object查看df前3行 OPEN HIGH LOW CLOSE 名称2018-06-28 3434.9441 3477.0565 3416.9476 3423.5255 HS3002018-06-29 3431.9619 3512.3834 3425.2159 3510.9845 HS3002018-07-02 3504.4571 3506.8996 3383.5006 3407.9638 HS300D:\anaconda\lib\site-packages\ipykernel_launcher.py:2: FutureWarning: .ix is deprecated. Please use.loc for label based indexing or.iloc for positional indexingSee the documentation here:http://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#ix-indexer-is-deprecated
1 | print('df的一列选取') |
df的一列选取2018-06-28 3434.94412018-06-29 3431.96192018-07-02 3504.45712018-07-03 3410.47672018-07-04 3398.77882018-07-05 3365.55472018-07-06 3347.06242018-07-09 3378.90562018-07-10 3464.9064Name: OPEN, dtype: float64df的两列同时选取 OPEN LOW2018-06-28 3434.9441 3416.94762018-06-29 3431.9619 3425.21592018-07-02 3504.4571 3383.50062018-07-03 3410.4767 3319.28892018-07-04 3398.7788 3359.08612018-07-05 3365.5547 3330.71132018-07-06 3347.0624 3295.72962018-07-09 3378.9056 3378.90562018-07-10 3464.9064 3437.2706
1 | print('截取df的前4行的close和low列') |
截取df的前4行的close和low列D:\anaconda\lib\site-packages\ipykernel_launcher.py:2: FutureWarning: .ix is deprecated. Please use.loc for label based indexing or.iloc for positional indexingSee the documentation here:http://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#ix-indexer-is-deprecated
CLOSE | LOW | |
---|---|---|
2018-06-28 | 3423.5255 | 3416.9476 |
2018-06-29 | 3510.9845 | 3425.2159 |
2018-07-02 | 3407.9638 | 3383.5006 |
2018-07-03 | 3409.2801 | 3319.2889 |
1 | print('截取df CLOSE大于等于3500的记录') |
截取df CLOSE大于等于3500的记录 OPEN HIGH LOW CLOSE 名称2018-06-29 3431.9619 3512.3834 3425.2159 3510.9845 HS300截取df CLOSE大于3300且LOW小于3400的记录 OPEN HIGH LOW CLOSE 名称2018-07-02 3504.4571 3506.8996 3383.5006 3407.9638 HS3002018-07-03 3410.4767 3422.0398 3319.2889 3409.2801 HS3002018-07-04 3398.7788 3418.3311 3359.0861 3363.7473 HS3002018-07-05 3365.5547 3398.4852 3330.7113 3342.4379 HS3002018-07-06 3347.0624 3396.2458 3295.7296 3365.1227 HS3002018-07-09 3378.9056 3459.3153 3378.9056 3459.1837 HS300
例如下面这个数据框data,其中就存在缺失值
1 | data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'], |
state | year | pop | |
---|---|---|---|
0 | Ohio | 2000 | 1.5 |
1 | Ohio | 2001 | NaN |
2 | Ohio | 2002 | 3.6 |
3 | None | 2001 | 2.4 |
4 | Nevada | 2002 | 2.9 |
1 | #删除含有缺失的行 |
state | year | pop | |
---|---|---|---|
0 | Ohio | 2000 | 1.5 |
2 | Ohio | 2002 | 3.6 |
4 | Nevada | 2002 | 2.9 |
1 | #表示该行都为缺失的行才删除 注意是这一行中的每一个元素都为缺失才删除这一行 |
state | year | pop | |
---|---|---|---|
0 | Ohio | 2000 | 1.5 |
1 | Ohio | 2001 | NaN |
2 | Ohio | 2002 | 3.6 |
3 | None | 2001 | 2.4 |
4 | Nevada | 2002 | 2.9 |
1 | #表示该列若都为缺失的列则删除,注意是这一列的每个元素都为缺失才会删除这一列 |
state | year | pop | |
---|---|---|---|
0 | Ohio | 2000 | 1.5 |
1 | Ohio | 2001 | NaN |
2 | Ohio | 2002 | 3.6 |
3 | None | 2001 | 2.4 |
4 | Nevada | 2002 | 2.9 |
1 | #表示保留至少存在3个非NaN的行,即如果某一行的非缺失值个数小于3个,则会被删除 |
state | year | pop | |
---|---|---|---|
0 | Ohio | 2000 | 1.5 |
2 | Ohio | 2002 | 3.6 |
4 | Nevada | 2002 | 2.9 |
1 | #表示保留至少存在3个非NaN的列,即如果某一列的非缺失值个数小于3个,则会被删除 |
state | year | pop | |
---|---|---|---|
0 | Ohio | 2000 | 1.5 |
1 | Ohio | 2001 | NaN |
2 | Ohio | 2002 | 3.6 |
3 | None | 2001 | 2.4 |
4 | Nevada | 2002 | 2.9 |
1 | data |
state | year | pop | |
---|---|---|---|
0 | Ohio | 2000 | 1.5 |
1 | Ohio | 2001 | NaN |
2 | Ohio | 2002 | 3.6 |
3 | None | 2001 | 2.4 |
4 | Nevada | 2002 | 2.9 |
1 | print('用0填充数据框中的缺失值,0是可选参数之一') |
用0填充数据框中的缺失值,0是可选参数之一
state | year | pop | |
---|---|---|---|
0 | Ohio | 2000 | 1.5 |
1 | Ohio | 2001 | 0.0 |
2 | Ohio | 2002 | 3.6 |
3 | 0 | 2001 | 2.4 |
4 | Nevada | 2002 | 2.9 |
1 | #填充缺失值 用缺失值所在列的前一个非NaN值来进行填充 |
state | year | pop | |
---|---|---|---|
0 | Ohio | 2000 | 1.5 |
1 | Ohio | 2001 | 1.5 |
2 | Ohio | 2002 | 3.6 |
3 | Ohio | 2001 | 2.4 |
4 | Nevada | 2002 | 2.9 |
1 | #用缺失值所在列的后一个非NaN来填充 |
state | year | pop | |
---|---|---|---|
0 | Ohio | 2000 | 1.5 |
1 | Ohio | 2001 | 3.6 |
2 | Ohio | 2002 | 3.6 |
3 | Nevada | 2001 | 2.4 |
4 | Nevada | 2002 | 2.9 |
氦核:这些填补都是什么鬼方法,无语。
1 | df |
OPEN | HIGH | LOW | CLOSE | 名称 | |
---|---|---|---|---|---|
2018-06-28 | 3434.9441 | 3477.0565 | 3416.9476 | 3423.5255 | HS300 |
2018-06-29 | 3431.9619 | 3512.3834 | 3425.2159 | 3510.9845 | HS300 |
2018-07-02 | 3504.4571 | 3506.8996 | 3383.5006 | 3407.9638 | HS300 |
2018-07-03 | 3410.4767 | 3422.0398 | 3319.2889 | 3409.2801 | HS300 |
2018-07-04 | 3398.7788 | 3418.3311 | 3359.0861 | 3363.7473 | HS300 |
2018-07-05 | 3365.5547 | 3398.4852 | 3330.7113 | 3342.4379 | HS300 |
2018-07-06 | 3347.0624 | 3396.2458 | 3295.7296 | 3365.1227 | HS300 |
2018-07-09 | 3378.9056 | 3459.3153 | 3378.9056 | 3459.1837 | HS300 |
2018-07-10 | 3464.9064 | 3474.1396 | 3437.2706 | 3467.5155 | HS300 |
1 | print('df按列OPEN降序排序') |
df按列OPEN降序排序
OPEN | HIGH | LOW | CLOSE | 名称 | |
---|---|---|---|---|---|
2018-07-06 | 3347.0624 | 3396.2458 | 3295.7296 | 3365.1227 | HS300 |
2018-07-05 | 3365.5547 | 3398.4852 | 3330.7113 | 3342.4379 | HS300 |
2018-07-09 | 3378.9056 | 3459.3153 | 3378.9056 | 3459.1837 | HS300 |
2018-07-04 | 3398.7788 | 3418.3311 | 3359.0861 | 3363.7473 | HS300 |
2018-07-03 | 3410.4767 | 3422.0398 | 3319.2889 | 3409.2801 | HS300 |
2018-06-29 | 3431.9619 | 3512.3834 | 3425.2159 | 3510.9845 | HS300 |
2018-06-28 | 3434.9441 | 3477.0565 | 3416.9476 | 3423.5255 | HS300 |
2018-07-10 | 3464.9064 | 3474.1396 | 3437.2706 | 3467.5155 | HS300 |
2018-07-02 | 3504.4571 | 3506.8996 | 3383.5006 | 3407.9638 | HS300 |
1 | print('df按列LOW升序排序') |
df按列LOW升序排序
OPEN | HIGH | LOW | CLOSE | 名称 | |
---|---|---|---|---|---|
2018-07-06 | 3347.0624 | 3396.2458 | 3295.7296 | 3365.1227 | HS300 |
2018-07-03 | 3410.4767 | 3422.0398 | 3319.2889 | 3409.2801 | HS300 |
2018-07-05 | 3365.5547 | 3398.4852 | 3330.7113 | 3342.4379 | HS300 |
2018-07-04 | 3398.7788 | 3418.3311 | 3359.0861 | 3363.7473 | HS300 |
2018-07-09 | 3378.9056 | 3459.3153 | 3378.9056 | 3459.1837 | HS300 |
2018-07-02 | 3504.4571 | 3506.8996 | 3383.5006 | 3407.9638 | HS300 |
2018-06-28 | 3434.9441 | 3477.0565 | 3416.9476 | 3423.5255 | HS300 |
2018-06-29 | 3431.9619 | 3512.3834 | 3425.2159 | 3510.9845 | HS300 |
2018-07-10 | 3464.9064 | 3474.1396 | 3437.2706 | 3467.5155 | HS300 |
1 | print('按列求均值') |
按列求均值OPEN 3415.227522HIGH 3451.655144LOW 3371.850689CLOSE 3416.640111dtype: float64
1 | print('按行求均值') |
按行求均值2018-06-28 3438.1184252018-06-29 3470.1364252018-07-02 3450.7052752018-07-03 3390.2713752018-07-04 3384.9858252018-07-05 3359.2972752018-07-06 3351.0401252018-07-09 3419.0775502018-07-10 3460.958025dtype: float64
下面的函数都是通过数据框.函数名(参数设置)来进行调用,一般的参数是axis=0/1,选择为0则是按行来实现函数,1则是按列来实现函数
序号 | 函数 | 函数含义 |
---|---|---|
1 | count | 计数非na值 |
2 | describe | 针对Series或个DataFrame列基本描述统计 |
3 | min、max | 计算最小值和最大值 |
4 | argmin、argmax | 获取到最大值和最小值的索引位置(整数) |
5 | idxmin、idxmax | 计算能够获取到最大值和最小值得索引值 |
6 | quantile | 计算样本的分位数(0到1) |
7 | sum | 求和 |
8 | mean | 求平均数 |
9 | median | 求中位数(50%分位数) |
10 | mad | 计算平均绝对离差 |
11 | var | 样本方差 |
12 | std | 样本标准差 |
13 | skew | 样本偏度(三阶矩) |
14 | kurt | 样本峰度(四阶矩) |
15 | cumsum | 样本累计和 |
16 | cummin,cummax | 样本累计最大值和累计最小值 |
17 | cumprod | 样本累计积 |
18 | diff | 计算一阶差分 |
19 | pct_change | 计算百分数变化 |
20 | corr | 计数相关性 |
下面介绍了三个函数来实现 DataFrame的拼接功能——concat函数,merge函数和join函数
通过Wind API可以获取到各种金融数据,可以使用代码生成器生成获取数据的函数代码。获取到数据后,可参考一下代码将数据转换为DataFrame格式
1 | from WindPy import * |
LASTRADEDAY_S | SEC_NAME | OPEN | HIGH | LOW | CLOSE | |
---|---|---|---|---|---|---|
2017-11-01 | 2017-11-01 | 平安银行 | 11.56 | 11.59 | 11.32 | 11.4 |
2017-11-02 | 2017-11-02 | 平安银行 | 11.36 | 11.58 | 11.26 | 11.54 |
2017-11-03 | 2017-11-03 | 平安银行 | 11.49 | 11.68 | 11.35 | 11.39 |
1 | wsd_data1 = w.wsd("000002.SZ", "lastradeday_s,sec_name,open,high,low,close", "2017-11-01", "2017-11-05", "") |
LASTRADEDAY_S | SEC_NAME | OPEN | HIGH | LOW | CLOSE | |
---|---|---|---|---|---|---|
2017-11-01 | 2017-11-01 | 万科A | 28.96 | 30.54 | 28.73 | 29.15 |
2017-11-02 | 2017-11-02 | 万科A | 29.3 | 29.48 | 28.68 | 29.45 |
2017-11-03 | 2017-11-03 | 万科A | 29.23 | 29.52 | 28.05 | 28.19 |
1 | print('按行拼接') |
按行拼接
LASTRADEDAY_S | SEC_NAME | OPEN | HIGH | LOW | CLOSE | |
---|---|---|---|---|---|---|
2017-11-01 | 2017-11-01 | 平安银行 | 11.56 | 11.59 | 11.32 | 11.4 |
2017-11-02 | 2017-11-02 | 平安银行 | 11.36 | 11.58 | 11.26 | 11.54 |
2017-11-03 | 2017-11-03 | 平安银行 | 11.49 | 11.68 | 11.35 | 11.39 |
2017-11-01 | 2017-11-01 | 万科A | 28.96 | 30.54 | 28.73 | 29.15 |
2017-11-02 | 2017-11-02 | 万科A | 29.3 | 29.48 | 28.68 | 29.45 |
2017-11-03 | 2017-11-03 | 万科A | 29.23 | 29.52 | 28.05 | 28.19 |
1 | print('按列拼接') |
按列拼接
LASTRADEDAY_S | SEC_NAME | OPEN | HIGH | LOW | CLOSE | LASTRADEDAY_S | SEC_NAME | OPEN | HIGH | LOW | CLOSE | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2017-11-01 | 2017-11-01 | 平安银行 | 11.56 | 11.59 | 11.32 | 11.4 | 2017-11-01 | 万科A | 28.96 | 30.54 | 28.73 | 29.15 |
2017-11-02 | 2017-11-02 | 平安银行 | 11.36 | 11.58 | 11.26 | 11.54 | 2017-11-02 | 万科A | 29.3 | 29.48 | 28.68 | 29.45 |
2017-11-03 | 2017-11-03 | 平安银行 | 11.49 | 11.68 | 11.35 | 11.39 | 2017-11-03 | 万科A | 29.23 | 29.52 | 28.05 | 28.19 |
pd.merge一般针对的是按列合并。
pd.merge(left, right, how=’inner’, on=None, left_on=None, right_on=None,
left_index=False, right_index=False, sort=True,
suffixes=(‘_x’, ‘_y’), copy=True, indicator=False)
left: 一个dataframe对象
right: 另一个dataframe对象
how: 可以是’left’, ‘right’, ‘outer’, ‘inner’. 默认为inner。
on: 列名,两个dataframe都有的列。如果不传参数,而且left_index和right_index也等于False,则默认把两者交叉/共有的列作为链接键(join keys)。可以是一个列名,也可以是包含多个列名的list。
left_on: 左边dataframe的列会用做keys。可以是列名,或者与dataframe长度相同的矩阵array。
right_on: 右边同上。
left_index: 如果为Ture,用左侧dataframe的index作为连接键。如果是多维索引,level数要跟右边相同才行。
right_index: 右边同上。
sort: 对合并后的数据框排序,以连接键。
suffixes: 一个tuple,包字符串后缀,用来加在重叠的列名后面。默认是(‘_x’,’_y’)。
copy: 默认Ture,复制数据。
indicator: 布尔型(True/FALSE),或是字符串。如果为True,合并之后会增加一列叫做_merge。是分类数据,用left_only, right_only, both来标记来自左边,右边和两边的数据。
参考:http://www.jianshu.com/p/dc8ba1c0eada
希望将上面两个 DataFrame left_data和right_data拼接起来,但要求是按照时间来进行拼接
1 | print('按LASTRADEDAY_S拼接,只保留共同的部分') |
按LASTRADEDAY_S拼接,只保留共同的部分
LASTRADEDAY_S | SEC_NAME_x | OPEN_x | HIGH_x | LOW_x | CLOSE_x | SEC_NAME_y | OPEN_y | HIGH_y | LOW_y | CLOSE_y | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2017-11-01 | 平安银行 | 11.56 | 11.59 | 11.32 | 11.4 | 万科A | 28.96 | 30.54 | 28.73 | 29.15 |
1 | 2017-11-02 | 平安银行 | 11.36 | 11.58 | 11.26 | 11.54 | 万科A | 29.3 | 29.48 | 28.68 | 29.45 |
2 | 2017-11-03 | 平安银行 | 11.49 | 11.68 | 11.35 | 11.39 | 万科A | 29.23 | 29.52 | 28.05 | 28.19 |
1 | print('按LASTRADEDAY_S拼接,但所有的数据都保留下来') |
按LASTRADEDAY_S拼接,但所有的数据都保留下来
LASTRADEDAY_S | SEC_NAME_x | OPEN_x | HIGH_x | LOW_x | CLOSE_x | SEC_NAME_y | OPEN_y | HIGH_y | LOW_y | CLOSE_y | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2017-11-01 | 平安银行 | 11.56 | 11.59 | 11.32 | 11.4 | 万科A | 28.96 | 30.54 | 28.73 | 29.15 |
1 | 2017-11-02 | 平安银行 | 11.36 | 11.58 | 11.26 | 11.54 | 万科A | 29.3 | 29.48 | 28.68 | 29.45 |
2 | 2017-11-03 | 平安银行 | 11.49 | 11.68 | 11.35 | 11.39 | 万科A | 29.23 | 29.52 | 28.05 | 28.19 |
1 | print('LASTRADEDAY_S,但所有的数据都保留下来,且生成一列来表示数据的来源') |
LASTRADEDAY_S,但所有的数据都保留下来,且生成一列来表示数据的来源
LASTRADEDAY_S | SEC_NAME_x | OPEN_x | HIGH_x | LOW_x | CLOSE_x | SEC_NAME_y | OPEN_y | HIGH_y | LOW_y | CLOSE_y | 数据来源 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2017-11-01 | 平安银行 | 11.56 | 11.59 | 11.32 | 11.4 | 万科A | 28.96 | 30.54 | 28.73 | 29.15 | both |
1 | 2017-11-02 | 平安银行 | 11.36 | 11.58 | 11.26 | 11.54 | 万科A | 29.3 | 29.48 | 28.68 | 29.45 | both |
2 | 2017-11-03 | 平安银行 | 11.49 | 11.68 | 11.35 | 11.39 | 万科A | 29.23 | 29.52 | 28.05 | 28.19 | both |
DataFrame.join(other, on=None, how=’left’, lsuffix=’’, rsuffix=’’, sort=False)
other:一个DataFrame、Series(要有命名),或者DataFrame组成的list。
on:列名,包含列名的list或tuple,或矩阵样子的列 (如果是多列,必须有MultiIndex)。 跟上面的几种方法一样,用来指明依据哪一列进行合并。 如果没有赋值,则依据两个数据框的index合并。
how:合并方式, {‘left’, ‘right’, ‘outer’, ‘inner’}, 默认‘left‘。
lsuffix:字符串。用于左侧数据框的重复列。 把重复列重新命名,原来的列名+字符串。 【如果有重复列,必须添加这个参数。】
rsuffix:同上。右侧。
sort:布尔型,默认False。如果为True,将链接键(on的那列)按字母排序。
参考:http://www.jianshu.com/p/dc8ba1c0eada
1 | print('注意到两个拼接的数据框,含有相同的列LASTRADEDAY_S,故重新命名了这两个列') |
注意到两个拼接的数据框,含有相同的列LASTRADEDAY_S,故重新命名了这两个列
LASTRADEDAY_S_left | SEC_NAME_left | OPEN_left | HIGH_left | LOW_left | CLOSE_left | LASTRADEDAY_S_right | SEC_NAME_right | OPEN_right | HIGH_right | LOW_right | CLOSE_right | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2017-11-01 | 2017-11-01 | 平安银行 | 11.56 | 11.59 | 11.32 | 11.4 | 2017-11-01 | 万科A | 28.96 | 30.54 | 28.73 | 29.15 |
2017-11-02 | 2017-11-02 | 平安银行 | 11.36 | 11.58 | 11.26 | 11.54 | 2017-11-02 | 万科A | 29.3 | 29.48 | 28.68 | 29.45 |
2017-11-03 | 2017-11-03 | 平安银行 | 11.49 | 11.68 | 11.35 | 11.39 | 2017-11-03 | 万科A | 29.23 | 29.52 | 28.05 | 28.19 |
有时候,希望能够剔除掉DataFrame中的重复记录。
1 | # 每股基本收益指标 |
FA_EPS_BASIC | |
---|---|
2017-08-07 | 0.31 |
2017-08-08 | 0.31 |
2017-08-09 | 0.31 |
2017-08-10 | 0.31 |
2017-08-11 | 0.68 |
2017-08-12 | 0.68 |
2017-08-13 | 0.68 |
2017-08-14 | 0.68 |
2017-08-15 | 0.68 |
1 | print('查看DataFrame中是否存在重复记录,标记为True的为重复记录') |
查看DataFrame中是否存在重复记录,标记为True的为重复记录2017-08-07 False2017-08-08 True2017-08-09 True2017-08-10 True2017-08-11 False2017-08-12 True2017-08-13 True2017-08-14 True2017-08-15 Truedtype: bool
1 | print('剔除数据框中的重复记录') |
剔除数据框中的重复记录
FA_EPS_BASIC | |
---|---|
2017-08-07 | 0.31 |
2017-08-11 | 0.68 |
氦核:这个函数很厉害。下面列几个用法:
1.根据DataFrame本身的某一列或多列内容进行分组聚合。
2.还可以利用for循环,对分组进行迭代。(下面举了个小栗子)
for name,group in df.groupby('key1'): print(name) print(group)
若仅使用一个变量name,会影响输出结果的索引层次表达方式,且结果为元组。
3.对聚合后的数据片段,进行格式类型转化
4.利用groupby,根据dtypes对列进行分组,此时,需指定axis=1
,否则,groupby默认根据axis=0
进行分组,而行数据由于类型不统一,故无法根据dtypes对列进行分组。
*#将聚合后df转化为字典格式,后根据df的数据类型对列进行分组* grouped=df.groupby(df.dtypes,axis=1) dict(list(grouped))
我们设定,如果当天收益率大于0,我们标记为up,反之为down。把收盘价大于11的标记为good,反之为bad。正式举例。
1 | from WindPy import * |
OPEN | CLOSE | PCT_CHG | standard | expression | |
---|---|---|---|---|---|
2018-04-20 | 11.51 | 11.35 | -1.046207 | down | good |
2018-04-23 | 11.30 | 11.57 | 1.938326 | up | good |
2018-04-24 | 11.63 | 11.86 | 2.506482 | up | good |
2018-04-25 | 11.76 | 11.68 | -1.517707 | down | good |
2018-04-26 | 11.66 | 11.42 | -2.226027 | down | good |
2018-04-27 | 11.49 | 10.85 | -4.991243 | down | bad |
1 | grouped = df4['CLOSE'].groupby(df4['standard']) |
<pandas.core.groupby.generic.SeriesGroupBy object at 0x0000020B26C63648>
氦核:上面这行是聚合后不适用配合函数的输出。
这是由于变量grouped是一个GroupBy对象,它实际上还没有进行任何计算,只是含有一些有关分组键df[‘key1’]
的中间数据而已,然后我们可以调用配合函数(如:.mean()方法)来计算分组平均值等。
因此,一般为方便起见可直接在聚合之后+“配合函数”,默认情况下,所有数值列都将会被聚合,虽然有时可能会被过滤为一个子集。
一般,如果对df直接聚合时,df.groupby([df['key1'],df['key2']]).mean()
(分组键为:Series)与df.groupby(['key1','key2']).mean()
(分组键为:列名)是等价的,输出结果相同。
但是,如果对df的指定列进行聚合时,df['data1'].groupby(df['key1']).mean()
(分组键为:Series),唯一方式。
此时,直接使用“列名”作分组键,提示“Error Key”。
注意:分组键中的任何缺失值都会被排除在结果之外。
1 | grouped.mean() |
standarddown 11.325up 11.715Name: CLOSE, dtype: float64
1 | means = df4['CLOSE'].groupby([df4['standard'],df4['expression']]).mean() |
standard expressiondown bad 10.850000 good 11.483333up good 11.715000Name: CLOSE, dtype: float64
对两个键进行了分组,得到的Series具有一个层次化索引。
1 | means.unstack() |
expression | bad | good |
---|---|---|
standard | ||
down | 10.85 | 11.483333 |
up | NaN | 11.715000 |
你还可以将列名用作分组键。
1 | df4.groupby('standard').mean() |
OPEN | CLOSE | PCT_CHG | |
---|---|---|---|
standard | |||
down | 11.605 | 11.325 | -2.445296 |
up | 11.465 | 11.715 | 2.222404 |
1 | df4.groupby([df4['standard'],df4['expression']]).mean() |
OPEN | CLOSE | PCT_CHG | ||
---|---|---|---|---|
standard | expression | |||
down | bad | 11.490000 | 10.850000 | -4.991243 |
good | 11.643333 | 11.483333 | -1.596647 | |
up | good | 11.465000 | 11.715000 | 2.222404 |
我们还可以用size方法,返回一个含有分组大小的Series。
1 | df4.groupby(['standard','expression']).size() |
standard expressiondown bad 1 good 3up good 2dtype: int64
GroupBy对象支持迭代,可以产生一组二元元组。
1 | for name,group in df4.groupby('standard'): |
down OPEN CLOSE PCT_CHG standard expression2018-04-20 11.51 11.35 -1.046207 down good2018-04-25 11.76 11.68 -1.517707 down good2018-04-26 11.66 11.42 -2.226027 down good2018-04-27 11.49 10.85 -4.991243 down badup OPEN CLOSE PCT_CHG standard expression2018-04-23 11.30 11.57 1.938326 up good2018-04-24 11.63 11.86 2.506482 up good
对于多重组件的情况,元素的第一个元素将会是有键值组成的元组:
1 | for (k1,k2), group in df4.groupby(['standard','expression']): |
down bad OPEN CLOSE PCT_CHG standard expression2018-04-27 11.49 10.85 -4.991243 down baddown good OPEN CLOSE PCT_CHG standard expression2018-04-20 11.51 11.35 -1.046207 down good2018-04-25 11.76 11.68 -1.517707 down good2018-04-26 11.66 11.42 -2.226027 down goodup good OPEN CLOSE PCT_CHG standard expression2018-04-23 11.30 11.57 1.938326 up good2018-04-24 11.63 11.86 2.506482 up good
当然,你可以对这些数据片段做任何操作。将这些数据片段做成一个字典:
1 | pieces = dict(list(df4.groupby('standard'))) |
{'down': OPEN CLOSE PCT_CHG standard expression 2018-04-20 11.51 11.35 -1.046207 down good 2018-04-25 11.76 11.68 -1.517707 down good 2018-04-26 11.66 11.42 -2.226027 down good 2018-04-27 11.49 10.85 -4.991243 down bad, 'up': OPEN CLOSE PCT_CHG standard expression 2018-04-23 11.30 11.57 1.938326 up good 2018-04-24 11.63 11.86 2.506482 up good}
groupby默认是在axis=0
上进行分组的。通过设置也可以对其他任何轴上进行分组。比如我们可以根据dtype对列进行分组:
1 | df4.dtypes |
OPEN float64CLOSE float64PCT_CHG float64standard objectexpression objectdtype: object
1 | grouped = df4.groupby(df4.dtypes,axis=1) |
{dtype('float64'): OPEN CLOSE PCT_CHG 2018-04-20 11.51 11.35 -1.046207 2018-04-23 11.30 11.57 1.938326 2018-04-24 11.63 11.86 2.506482 2018-04-25 11.76 11.68 -1.517707 2018-04-26 11.66 11.42 -2.226027 2018-04-27 11.49 10.85 -4.991243, dtype('O'): standard expression 2018-04-20 down good 2018-04-23 up good 2018-04-24 up good 2018-04-25 down good 2018-04-26 down good 2018-04-27 down bad}
1 | df4.groupby(['standard','expression'])[['CLOSE']].mean() # 在['CLOSE']前后再多加一组[]即可 |
氦核:再加一个中括号。
CLOSE | ||
---|---|---|
standard | expression | |
down | bad | 10.850000 |
good | 11.483333 | |
up | good | 11.715000 |
或者是以分组的Series:
1 | s_grouped = df4.groupby(['standard','expression'])['CLOSE'] |
standard expressiondown bad 10.850000 good 11.483333up good 11.715000Name: CLOSE, dtype: float64
除数组以外,分组信息还可以以其他形式存在。我们新构建一个DataFrame。
1 | error_code,df_wss = w.wss("000001.SZ,000088.SZ,002626.SZ,600021.SH,600036.SH", "open,high,low,volume,amt,pct_chg", "tradeDate=2018-05-29;priceAdj=1;cycle=1",usedf=True) |
OPEN | HIGH | LOW | VOLUME | AMT | PCT_CHG | |
---|---|---|---|---|---|---|
000001.SZ | 10.58 | 10.63 | 10.35 | 88949497.0 | 9.303870e+08 | -1.983003 |
000088.SZ | 7.65 | 7.79 | 7.61 | 9731389.0 | 7.493758e+07 | -0.260756 |
002626.SZ | 19.13 | 19.72 | 18.97 | 7058799.0 | 1.367769e+08 | -0.679561 |
600021.SH | 8.15 | 8.20 | 8.10 | 2065681.0 | 1.685831e+07 | -0.368098 |
600036.SH | 28.65 | 28.98 | 28.41 | 48053415.0 | 1.376605e+09 | 0.486111 |
1 | # 添加几个空值NAN |
D:\anaconda\lib\site-packages\ipykernel_launcher.py:2: FutureWarning: .ix is deprecated. Please use.loc for label based indexing or.iloc for positional indexingSee the documentation here:http://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#ix-indexer-is-deprecated
氦核:依然不建议使用ix。
OPEN | HIGH | LOW | VOLUME | AMT | PCT_CHG | |
---|---|---|---|---|---|---|
000001.SZ | 10.58 | 10.63 | 10.35 | 88949497.0 | 9.303870e+08 | -1.983003 |
000088.SZ | 7.65 | 7.79 | 7.61 | 9731389.0 | 7.493758e+07 | -0.260756 |
002626.SZ | NaN | NaN | 18.97 | 7058799.0 | 1.367769e+08 | -0.679561 |
600021.SH | 8.15 | 8.20 | 8.10 | 2065681.0 | 1.685831e+07 | -0.368098 |
600036.SH | 28.65 | 28.98 | 28.41 | 48053415.0 | 1.376605e+09 | 0.486111 |
假设已知列的分组关系,并希望根据分组计算列的总和:
1 | mapping = {'OPEN':'r','HIGH':'r','LOW':'b','VOLUME':'b','AMT':'b','PCT_CHG':'o'} |
只需要将这个字典传给groupby即可:(行方向)
1 | by_colum = df_wss.groupby(mapping,axis=1) |
b | o | r | |
---|---|---|---|
000001.SZ | 1.019336e+09 | -1.983003 | 21.21 |
000088.SZ | 8.466897e+07 | -0.260756 | 15.44 |
002626.SZ | 1.438358e+08 | -0.679561 | 0.00 |
600021.SH | 1.892400e+07 | -0.368098 | 16.35 |
600036.SH | 1.424658e+09 | 0.486111 | 57.63 |
Series也有这样的功能。
1 | map_series = pd.Series(mapping) |
OPEN rHIGH rLOW bVOLUME bAMT bPCT_CHG odtype: object
1 | df_wss.groupby(map_series,axis=1).count() |
b | o | r | |
---|---|---|---|
000001.SZ | 3 | 1 | 2 |
000088.SZ | 3 | 1 | 2 |
002626.SZ | 3 | 1 | 0 |
600021.SH | 3 | 1 | 2 |
600036.SH | 3 | 1 | 2 |
层次化索引数据最方便的地方就是在于它能够根据索引级别进行聚合。要实现该目的,通过level关键字传入级别编号或名称即可:
1 | df_wss |
OPEN | HIGH | LOW | VOLUME | AMT | PCT_CHG | |
---|---|---|---|---|---|---|
000001.SZ | 10.58 | 10.63 | 10.35 | 88949497.0 | 9.303870e+08 | -1.983003 |
000088.SZ | 7.65 | 7.79 | 7.61 | 9731389.0 | 7.493758e+07 | -0.260756 |
002626.SZ | NaN | NaN | 18.97 | 7058799.0 | 1.367769e+08 | -0.679561 |
600021.SH | 8.15 | 8.20 | 8.10 | 2065681.0 | 1.685831e+07 | -0.368098 |
600036.SH | 28.65 | 28.98 | 28.41 | 48053415.0 | 1.376605e+09 | 0.486111 |
1 | columns = pd.MultiIndex.from_arrays([['行情','行情','行情','量','量','幅度'], |
s1 | 行情 | 量 | 幅度 | |||
---|---|---|---|---|---|---|
s2 | OPEN | HIGH | LOW | VOLUME | AMT | PCT_CHG |
000001.SZ | 10.58 | 10.63 | 10.35 | 88949497.0 | 9.303870e+08 | -1.983003 |
000088.SZ | 7.65 | 7.79 | 7.61 | 9731389.0 | 7.493758e+07 | -0.260756 |
002626.SZ | NaN | NaN | 18.97 | 7058799.0 | 1.367769e+08 | -0.679561 |
600021.SH | 8.15 | 8.20 | 8.10 | 2065681.0 | 1.685831e+07 | -0.368098 |
600036.SH | 28.65 | 28.98 | 28.41 | 48053415.0 | 1.376605e+09 | 0.486111 |
1 | hier_df.groupby(level='s1',axis=1).count() |
s1 | 幅度 | 行情 | 量 |
---|---|---|---|
000001.SZ | 1 | 3 | 2 |
000088.SZ | 1 | 3 | 2 |
002626.SZ | 1 | 1 | 2 |
600021.SH | 1 | 3 | 2 |
600036.SH | 1 | 3 | 2 |
(完)
依然鸣谢:某大哥假粉。愿四下空虚的灵魂皆能得以慰藉。
]]>最后编辑于:2020.02.05 12:30
Numpy库是Python的一种开源的数值计算扩展。
Numpy可用来存储和处理大型矩阵,比Python自身的嵌套列表结构要高效很多。
据说Numpy将Pyhon变成了一种免费的更强大的Matlab系统。
本文介绍性文字转载自万旷网。氦核感觉notebook形式更适合学习,有机会把丘比特文件给大家附上。
Numpy库包含了:
>
强大的N维数组对象
精密的函数
连接C/C++和Fortran代码的工具
常用的线性代数,傅里叶变换和随机数生成
1 | #首先导入 numpy 库 |
氦核:惯用的导入法,最好延用。
数组array和列表list类似,但是数据array可以定义维度,且适合做数学代数运算
数据使用windapi提取。
1 | from WindPy import * |
氦核:取的是收盘价和开盘价。
.ErrorCode=0.Codes=[000002.SZ].Fields=[CLOSE,OPEN].Times=[20180705,20180706,20180709,20180710,20180711].Data=[[23.05,23.21,24.01,24.15,23.46],[23.02,23.34,23.37,24.2,23.48]]
1 | a1 = np.array(a.Data[0]) |
氦核:len可以求数组长度,指数组里有几个list。
这是一个一维数组: [8.6 8.66 9.03 8.98 8.78]这是一个二维数组: [[23.05 23.21 24.01 24.15 23.46] [23.02 23.34 23.37 24.2 23.48]]查看a1的长度: 5查看a2的长度: 2
1 | a2 |
array([[23.05, 23.21, 24.01, 24.15, 23.46], [23.02, 23.34, 23.37, 24.2 , 23.48]])
1 | a2[0,1] |
23.21
1 | '''数组元素整数转化为浮点数''' |
氦核:dtype可以查看类型,astype可以转换类型。不同类型变换后会产生不同结果。
数组类型: float64改变数组类型后: int32
1 | '''字符串数字转化为浮点数''' |
['1.11' '2.22' '3.33'] <U4[1.11 2.22 3.33] float64
1 | '''字符串数字转化为浮点数''' |
['1.11' '2.22' '3.33'] <U4[1.11 2.22 3.33] float64
1 | print(a2) |
[[23.05 23.21 24.01 24.15 23.46] [23.02 23.34 23.37 24.2 23.48]]第二行第三列元素(第二行索引为1,第三列索引为2): 23.37倒数第一行(注意索引为-1): [23.02 23.34 23.37 24.2 23.48]第三列(索引为2): [24.01 23.37]a2形状: (2, 5)a2形状重构: [[23.05 23.21] [24.01 24.15] [23.46 23.02] [23.34 23.37] [24.2 23.48]]
1 | print('维度解锁:',a2.ravel()) # ravel()函数可以将高维数组转化为一维数组 |
维度解锁: [23.05 23.21 24.01 24.15 23.46 23.02 23.34 23.37 24.2 23.48]按列求和: [46.07 46.55 47.38 48.35 46.94]按列求积: [530.611 541.7214 561.1137 584.43 550.8408]全局最大值: 24.2 全局最小值: 23.02按行求最大值: [23.05 23.34 24.01 24.2 23.48] 按列求最小值: [23.05 23.02]按列求均值: [23.035 23.275 23.69 24.175 23.47 ]按行求标准差: [0.015 0.065 0.32 0.025 0.01 ]
氦核:这些计算都是可以接受方向的。按列或按行。
1 | print('原矩阵:\n',a2) |
原矩阵: [[23.05 23.21 24.01 24.15 23.46] [23.02 23.34 23.37 24.2 23.48]]按列求和: [46.07 46.55 47.38 48.35 46.94]按行求均值: [23.576 23.482]按行累加: [[ 23.05 46.26 70.27 94.42 117.88] [ 23.02 46.36 69.73 93.93 117.41]]
1 | print('矩阵所有元素求指数:\n',np.exp(a2)) |
氦核:np.exp功能是“求e的幂次方”。
矩阵所有元素求指数: [[1.02444302e+10 1.20219502e+10 2.67553422e+10 3.07759692e+10 1.54364896e+10] [9.94166153e+09 1.36909381e+10 1.41078893e+10 3.23538868e+10 1.57483274e+10]]矩阵所有元素求根号: [[4.80104155 4.81767579 4.9 4.91426495 4.84355242] [4.79791621 4.83114893 4.83425279 4.91934955 4.84561658]]
小数位数控制和取整
1 | print('小数位数:\n',a1.round(decimals=2)) #控制小数位数 |
小数位数: [8.6 8.66 9.03 8.98 8.78]
1 | print('原数组:\n',a1) |
原数组: [8.6 8.66 9.03 8.98 8.78]向上取整: [8. 8. 9. 8. 8.]向下取整: [ 9. 9. 10. 9. 9.]四舍五入(控制小数为2位): [8.6 8.66 9.03 8.98 8.78]
函数 | 说明 |
---|---|
abs、fabs | 计算整数、浮点数或复数的绝对值。对于非复数,使用fabs更快 |
sqrt、square、exp | 计算各元素的平方根、平方、指数𝑒𝑥 |
log、log10、log2、log1p | 自然对数、底数10的对数、底数2的对数、𝑙𝑛(1+𝑥) |
sign | 计算各元素的正负号:正1,零0,负-1 |
ceil | 计算各元素的取整:大于等于该数的最小整数 |
floor | 计算各元素的取整:小于等于该数的最大整数 |
rint | 各元素四舍五入最接近的整数,dtype不变 |
modf | 将数组各元素的小数和整数部分以两个独立数组的形式返回 |
isnan、isfinite、isinf | 判断各元素是否为NaN、是否有穷、是否为无穷 |
cos、cosh、sin、sinh、tan、tanh | 一般和双曲型的三角函数 |
arccos、arccosh、arcsin、arcsinh、arctan、arctanh | 反三角函数 |
sum、mean | 数组全部或者按某个轴的方向进行求和、求均值 |
std、var | 标准差、方差,自由度可以调整 |
min、max、argmin、argmax | 最小和最大值、最小和最大元素的索引 |
cumsum、cumprod | 数组全部或者按某个轴的方向进行累计和、累计积 |
1 | c, r = np.array([1,2,3,4]), np.array([2,3,4,5]) |
[1 2 3 4][2 3 4 5]
1 | print('数组相加:',c + r) |
数组相加: [3 5 7 9]数组相乘: [ 2 6 12 20]数组乘方: [ 1 8 81 1024]数组判断: [False True True True]向量内积: 40
1 | print('取两个数组中的较大值组成新的数组:',np.maximum(c,r)) |
取两个数组中的较大值组成新的数组: [2 3 4 5]取两个数组中的较小者组成新的数组: [1 2 3 4]
1 | x1 = np.array([True,False,True]) |
氦核:逻辑运算一样很简单,logical_xor是异或运算。一般逻辑函数用于检验数组内容,筛选出需要的元素(通常得到的是位置)。可以完成“检查a中元素是否存在于b中”这样的问题。简便操作详细见下面集合运算。
[ True False True][False False True][False False True][ True False True][ True False False]
函数 | 说明 |
---|---|
add、multiply | 数组中对应的元素相加、相乘 |
substract | 第一个数组减去第二个数组中的元素 |
divide、floor_divide | 除法、向下圆整除法(余数直接舍弃) |
power | 对于第一个数组中的元素,根据第二个数组中的对应元素,进行幂运算 |
maximum、fmax | 元素级的最大值、fmax功能相同只是忽略NaN |
minimum、fmin | 元素级的最小值、fmin功能相同只是忽略NaN |
mod | 元素级的求余 |
copysign | 将第二个数组中的值的符号复制给第一个数组中的值 |
greater、greater_equal、less、less_equal、equal、not_equal | 元素级的比较运算,产生True或者False为元素的数组 |
logical_and、logical_or、logical_xor | 元素级的逻辑判断(且、或者、不等于) |
1 | x = np.array([1,2,3,3,3,3,4,5,10,10,20,30]) |
数组x中的唯一元素: [ 1 2 3 4 5 10 20 30]数组x和y的公共元素: [ 1 2 3 10 20]数组x和y的并集: [ 1 2 3 4 5 10 20 30 40 100]数组x中的元素是否包含于y: [ True True True True True True False False True True True False]集合差_在x中而不在y中的元素: [ 4 5 30]只存在某个数组中,而不同时存在于两个数组中: [ 4 5 30 40 100]
1 | #还是以000001收盘价为例 |
array([8.6 , 8.66, 9.03, 8.98, 8.78])
1 | # 如果我们想 按偶数来选取 即选择数组中的0,2,4,6,8 |
氦核:上面片段中的2代表步长。
[8.6 9.03 8.78][8.66 8.98]
1 | #我们取多个指标看一下 |
氦核:开,高,低,收。
.ErrorCode=0.Codes=[000858.SZ].Fields=[OPEN,HIGH,LOW,CLOSE,PCT_CHG].Times=[20180705,20180706,20180709,20180710,20180711].Data=[[71.69,69.9,71.58,73.57,71.2],[72.71,71.98,73.55,74.13,72.64],[69.7,68.88,70.4,72.08,70.93],[70.82,70.62,73.54,73.37,72.08],[0.16973125884014886,1.5822798147378172,4.134806003964902,-0.23116671199347652,-1.7582118031893383]]
1 | w1 = np.array(w.Data) |
array([[71.69 , 69.9 , 71.58 , 73.57 , 71.2 ], [72.71 , 71.98 , 73.55 , 74.13 , 72.64 ], [69.7 , 68.88 , 70.4 , 72.08 , 70.93 ], [70.82 , 70.62 , 73.54 , 73.37 , 72.08 ], [ 0.16973126, 1.58227981, 4.134806 , -0.23116671, -1.7582118 ]])
1 | print('截取第1行第4,5个元素:\n',w1[0, 3:5]) |
截取第1行第4,5个元素: [73.57 71.2 ]截取第5行至最后,第5列至最后的元素: [[-1.7582118]]截取第3,5行,第1,3,5列 [[69.7 70.4 70.93 ] [ 0.16973126 4.134806 -1.7582118 ]]
1 | from WindPy import * |
.ErrorCode=0.Codes=[000001.SZ].Fields=[PCT_CHG].Times=[20180705,20180706,20180709,20180710,20180711].Data=[[-0.11614401858303243,0.6976744186046501,4.272517321016162,-0.5537098560354228,-2.2271714922049117]]
1 | sy2 = np.array(sy.Data[0]) |
array([-0.11614402, 0.69767442, 4.27251732, -0.55370986, -2.22717149])
1 | # 对一个数组array,想找到其中大于0的数所在的索引位置 可以用where函数 |
大于0元素所在的索引: (array([1, 2], dtype=int64),)
1 | # 对于exp这个数组,希望对其按元素大小进行排序 |
从小到大排序: [-2.22717149 -0.55370986 -0.11614402 0.69767442 4.27251732]
1 | print('排序后元素所在的原索引位置',np.argsort(sy2)) |
排序后元素所在的原索引位置 [4 3 0 1 2]
1 | a1 |
array([8.6 , 8.66, 9.03, 8.98, 8.78])
1 | a3 = np.array(w.wsd("000858.SZ", "close", "2018-07-05", "2018-07-11", "").Data)[0] |
array([70.82, 70.62, 73.54, 73.37, 72.08])
1 | print('纵向拼接:\n',np.vstack((a1,a3))) |
纵向拼接: [[ 8.6 8.66 9.03 8.98 8.78] [70.82 70.62 73.54 73.37 72.08]]横向拼接: [ 8.6 8.66 9.03 8.98 8.78 70.82 70.62 73.54 73.37 72.08]
使用np.r_和np.c_也可以实现拼接的功能
注意纵向拼接的时候,np.c_产生的结果是5∗2,而np.r_产生的结果是2∗5
1 | print('横向拼接:\n',np.r_[a1,a3]) |
横向拼接: [ 8.6 8.66 9.03 8.98 8.78 70.82 70.62 73.54 73.37 72.08]纵向拼接: [[ 8.6 70.82] [ 8.66 70.62] [ 9.03 73.54] [ 8.98 73.37] [ 8.78 72.08]]
1 | a4 = np.array(w.wsd("000858.SZ", "close,open,low", "2018-07-05", "2018-07-11", "").Data) |
array([[70.82, 70.62, 73.54, 73.37, 72.08], [71.69, 69.9 , 71.58, 73.57, 71.2 ], [69.7 , 68.88, 70.4 , 72.08, 70.93]])
1 | print('横向分解为5个数组:\n',np.hsplit(a4,5)) |
横向分解为5个数组: [array([[70.82], [71.69], [69.7 ]]), array([[70.62], [69.9 ], [68.88]]), array([[73.54], [71.58], [70.4 ]]), array([[73.37], [73.57], [72.08]]), array([[72.08], [71.2 ], [70.93]])]纵向分解为3个数组: [array([[70.82, 70.62, 73.54, 73.37, 72.08]]), array([[71.69, 69.9 , 71.58, 73.57, 71.2 ]]), array([[69.7 , 68.88, 70.4 , 72.08, 70.93]])]
在工作或者学习中,有些数组是我们常用的,利用numpy中的函数可以容易地产生这些数组。
1 | # np.arange()函数 终止数并不产生 |
[1 2 3 4 5 6 7 8 9][1. 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2. 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3. 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 4. 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 5. 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 6. 6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9 7. 7.1 7.2 7.3 7.4 7.5 7.6 7.7 7.8 7.9 8. 8.1 8.2 8.3 8.4 8.5 8.6 8.7 8.8 8.9 9. 9.1 9.2 9.3 9.4 9.5 9.6 9.7 9.8 9.9]
1 | #在指定区间返回均匀间隔的数字 |
[ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.][-1. -0.89473684 -0.78947368 -0.68421053 -0.57894737 -0.47368421 -0.36842105 -0.26315789 -0.15789474 -0.05263158 0.05263158 0.15789474 0.26315789 0.36842105 0.47368421 0.57894737 0.68421053 0.78947368 0.89473684 1. ]
1 | print('元素都为1的方阵:\n',np.ones((3,3))) |
元素都为1的方阵: [[1. 1. 1.] [1. 1. 1.] [1. 1. 1.]]元素都为0的方阵: [[0. 0. 0.] [0. 0. 0.] [0. 0. 0.]]单位阵: [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]]
该函数的作用是重复某个对象为一定的结构
1 | short = np.arange(1,4,1) |
[1 2 3][1 2 3 1 2 3 1 2 3]
1 | small = np.eye(2) |
[[1. 0.] [0. 1.]][[1. 0. 1. 0.] [0. 1. 0. 1.] [1. 0. 1. 0.] [0. 1. 0. 1.]]
1 | print('自然底数:',np.e) |
自然底数: 2.718281828459045缺失值: nan无穷大: inf圆周率: 3.141592653589793
1 | print('一维正态随机数:\n',np.random.randn(5)) |
一维正态随机数: [-0.65240628 1.07426523 -0.86730208 -0.91339241 1.20066541]二维正态随机数: [[ 0.9663628 -1.32891237] [-0.54264665 -1.29035995]]二维0-1均匀分布随机数: [[0.52527192 0.17719291] [0.10103986 0.41623399]]5个10-20的均匀随机整数: [17 18 14 12 11]二维均匀随机整数: [[38 24] [24 38]]
numpy.random函数
函数 | 说明 |
---|---|
seed | 随机数生成器的种子 |
permutation | 序列的随机排列或者随机排列的范围,不改变原数组 |
shuffle | 序列就地随机排列,改变原数组 |
rand | 均匀分布样本值 |
randint | 给定上下限随机产生整数 |
randn | 正态分布样本值 |
binomial | 二项分布样本值 |
normal | 正态分布样本值 |
beta | beta分布样本值 |
chisquare | 卡方分布样本值 |
gamma | Gamma分布样本值 |
uniform | [0,1)均匀分布样本值 |
choice | 从数组中随机选择若干个元素 |
1 | a = np.arange(1,11,1) |
[ 1 2 3 4 5 6 7 8 9 10]随机打乱a中的元素顺序: [10 3 2 6 1 9 8 7 4 5]
1 | print(a) |
[10 3 2 6 1 9 8 7 4 5]随机从a中选取5个元素: [5 3 8 9 4]
1 | x = np.random.randint(1,10,(3,3)) |
原矩阵: [[4 3 6] [2 7 1] [2 3 8]]矩阵对角线: [4 7 8]矩阵上三角: [[4 3 6] [0 7 1] [0 0 8]]矩阵下三角: [[4 0 0] [2 7 0] [2 3 8]]矩阵的迹: 19矩阵的转置: [[4 2 2] [3 7 3] [6 1 8]]
1 | x = np.random.randint(1,10,(3,3)) |
原矩阵: [[1 1 6] [7 3 3] [7 6 5]]矩阵元素向右循环移动2位: [[6 5 1] [1 6 7] [3 3 7]]
1 | # numpy下的子模块linalg是一个线性代数运算库,关于矩阵运算主要使用该库来完成 |
1 | # 以000001行情数据为例 |
array([[9.03, 8.98, 8.78], [8.69, 9.02, 8.76], [8.68, 8.89, 8.68]])
1 | print('原矩阵:\n',a5) |
原矩阵: [[9.03 8.98 8.78] [8.69 9.02 8.76] [8.68 8.89 8.68]]矩阵的行列式: 0.0967539999999976矩阵的逆: [[ 4.31196643 1.11416582 -5.48607809] [ 6.27984373 22.42801331 -28.98691527] [-10.74374186 -24.08479236 35.28949708]]矩阵的特征值分解: (array([2.65036938e+01, 2.08824580e-01, 1.74815890e-02]), array([[-0.58362007, -0.78625042, -0.09211654], [-0.57657184, 0.5886538 , -0.64732544], [-0.57179763, 0.18787491, 0.75662693]]))矩阵的奇异值分解: (array([[-0.58355077, 0.78217367, -0.21834112], [-0.5766266 , -0.58842029, -0.56680096], [-0.57181314, -0.20485584, 0.79439526]]), array([2.65057682e+01, 2.11310491e-01, 1.72745790e-02]), array([[-0.57510827, -0.58571691, -0.57112711], [ 0.81163598, -0.49595222, -0.30867203], [-0.10245733, -0.64106715, 0.76061515]]))
numpy.linalg函数
函数 | 说明 |
---|---|
diag | 以一维数组的形式返回方阵的对角线元素或将一维数组转化为方阵 |
dot、trace、det | 矩阵乘法、矩阵的迹运算、矩阵行列式 |
eig、inv、pinv | 方阵的特征值和特征向量、方阵的逆、矩阵的Moore-Penrose伪逆 |
qr、svd | 矩阵的QR分解、奇异值分解 |
solve | 解线性方程组 $𝑋\beta = 𝑦$,其中$𝑋$为方阵 |
lstsq | 计算$𝑋\beta = 𝑦$的最小二乘解 |
1 | import matplotlib.pyplot as plt # 导入作图库 为了更好展示曲线拟合的结果 |
例如,对于下面的这些散点进行多项式拟合。观察散点的形态,采用直线取拟合
1 | x = np.linspace(-10,10,100) |
1 | from numpy import polyfit,poly1d |
1 | coef_fit = polyfit(x, y, 1) #进行线性拟合 1代表的是多项式拟合的多项式的阶数 这里指的是线性拟合 |
array([1.96386726, 1.11375232])
1 | fig = plt.subplots(figsize=(14,8)) |
D:\anaconda\lib\site-packages\matplotlib\backends\backend_agg.py:211: RuntimeWarning: Glyph 30495 missing from current font. font.set_text(s, 0.0, flags=flags)D:\anaconda\lib\site-packages\matplotlib\backends\backend_agg.py:211: RuntimeWarning: Glyph 23454 missing from current font. font.set_text(s, 0.0, flags=flags)D:\anaconda\lib\site-packages\matplotlib\backends\backend_agg.py:211: RuntimeWarning: Glyph 25955 missing from current font. font.set_text(s, 0.0, flags=flags)D:\anaconda\lib\site-packages\matplotlib\backends\backend_agg.py:211: RuntimeWarning: Glyph 28857 missing from current font. font.set_text(s, 0.0, flags=flags)D:\anaconda\lib\site-packages\matplotlib\backends\backend_agg.py:211: RuntimeWarning: Glyph 25311 missing from current font. font.set_text(s, 0.0, flags=flags)D:\anaconda\lib\site-packages\matplotlib\backends\backend_agg.py:211: RuntimeWarning: Glyph 21512 missing from current font. font.set_text(s, 0.0, flags=flags)D:\anaconda\lib\site-packages\matplotlib\backends\backend_agg.py:211: RuntimeWarning: Glyph 30452 missing from current font. font.set_text(s, 0.0, flags=flags)D:\anaconda\lib\site-packages\matplotlib\backends\backend_agg.py:211: RuntimeWarning: Glyph 32447 missing from current font. font.set_text(s, 0.0, flags=flags)D:\anaconda\lib\site-packages\matplotlib\backends\backend_agg.py:180: RuntimeWarning: Glyph 30495 missing from current font. font.set_text(s, 0, flags=flags)D:\anaconda\lib\site-packages\matplotlib\backends\backend_agg.py:180: RuntimeWarning: Glyph 23454 missing from current font. font.set_text(s, 0, flags=flags)D:\anaconda\lib\site-packages\matplotlib\backends\backend_agg.py:180: RuntimeWarning: Glyph 25955 missing from current font. font.set_text(s, 0, flags=flags)D:\anaconda\lib\site-packages\matplotlib\backends\backend_agg.py:180: RuntimeWarning: Glyph 28857 missing from current font. font.set_text(s, 0, flags=flags)D:\anaconda\lib\site-packages\matplotlib\backends\backend_agg.py:180: RuntimeWarning: Glyph 25311 missing from current font. font.set_text(s, 0, flags=flags)D:\anaconda\lib\site-packages\matplotlib\backends\backend_agg.py:180: RuntimeWarning: Glyph 21512 missing from current font. font.set_text(s, 0, flags=flags)D:\anaconda\lib\site-packages\matplotlib\backends\backend_agg.py:180: RuntimeWarning: Glyph 30452 missing from current font. font.set_text(s, 0, flags=flags)D:\anaconda\lib\site-packages\matplotlib\backends\backend_agg.py:180: RuntimeWarning: Glyph 32447 missing from current font. font.set_text(s, 0, flags=flags)
从上图可以看到,直线拟合的结果还是比较好的(报错可能是汉字bug
1 | f = poly1d(coef_fit) #也可以直接产生拟合的函数解析式 |
拟合函数: 1.964 x + 1.114
氦核:numpy库很有“大计算器”的味道了,其实不会用的时候再去查也可以,主要是应该了解有什么基础功能,否则就会出现“自己实现某些基础算法”的乌龙(某种意义上也是好事,笑)。一起加油吧。
(完)
]]>最后编辑于:2020.02.05 12:30
windAPI是一个很好的工具,可以不通过客户端获取数据(不过前提是要有土豪的账号加持,笑)本文大部分介绍性文字转载自万旷网。本文的分析全部通过python完成。配置安装略过不表,请致电客服经理小哥哥(声音奶声奶气有点温柔!
目前万矿网支持的API函数有:
1、WSD日期序列函数:支持股票、债券、基金、期货、指数等多种证券的基本资料、股东信息、市场行情、证券分析、预测评级、财务数据等各种数据。WSD可以支持取 多品种单指标
或者 单品种多指标
的时间序列数据。(氦核:可以说是最重要的函数)
2、WSS多维函数:同样支持股票、债券、基金、期货、指数等多种证券的基本资料、股东信息、市场行情、证券分析、预测评级、财务数据等各种数据。但是WSS支持取多品种多指标某个时间点的截面数据。
3、WSQ行情数据函数:支持股票、债券、基金、期货、指数等多种证券品种的实时行情数据,既可以选择获取一次性的快照数据,也可以选择订阅数据(即交易所有新的行情就推送)。
4、WSET数据集:支持股票、债券、基金、期货、指数等多种证券品种板块成分、指数历史成分股以及权重,以及各种市场常用报表的获取。
5、TDays 日期函数:日期函数包含日期序列函数(TDays)、日期偏移函数(TDaysOffset) 以及日期区间统计函数(TDaysCount)。
下面为大家介绍各个函数的详细用法。
注: 建议用户在使用取数函数时直接借助API函数生成相应的取数代码,然后修改其中的参数使其满足自己的取数需求。(氦核注:这个很好使,点点点就能拿到代码。)
该命令用来获取选定证券品种的历史序列数据,包括日间的行情数据、基本面数据以及技术数据指标。(氦核注:有些数据被限制获取的数量了,有些是最多五十个,这倒是很不方便。如果看实盘还好,但是做分析就不舒服了。)
WSD函数结构 w.wsd(security,fields,startdate,enddate,option)
Element | Type | Description | ||
---|---|---|---|---|
证券(必选) | Security | String | 获取数据的证券列表 | 范例1:’600030.SH’说明:证券列表支持Wind代码及证券转换类工具函数输出的Wind代码结果 |
指标(必选) | Fields | String | 获取数据的指标列表 | 范例1:’CLOSE,HIGH,LOW,OPEN’ |
起始日期(必选) | StartDate | 时间序列的起始日期 | 范例1:’2017-01-01’,’-5w’说明:支持日期类工具函数输出的标准日期结果,支持相对日期宏表达方式,日期宏具体使用方式参考’日期宏’部分内容 | |
截止日期(必选) | EndDate | 时间序列的截止日期,若为空默认为系统当前日期 | 范例1:’2017-05-30’,Sys.Date(),支持相对日期,比如’0w’; 不输入的话为当前时间说明:支持日期类工具函数输出的标准日期结果,支持相对日期宏表达方式 | |
指标参数(可选) | Parameter/Value | String | 提取指标时使用的参数名/指定参数的值 | 范例:’TRADE_DATE=20110301;FUND_DATE=20101231’说明:多指标参数支持在不同引号内分开取值 |
变频参数(可选) | Period | String | 每天一值:D/每周一值:W/每月一值:M/每季度一值:Q/每半年一值:S/每年一值:Y | 范例:’Period=D’ ,默认Period=D |
输出日期(可选) | Days | String | 所有工作日:Weekdays/所有日历日:Alldays/排除所有非交易日:Trading | 范例:’Days=Trading’,默认Days=Trading |
填充方式(可选) | Fill | String | 沿用之前数据:Previous/返回空值:Blank | 范例:’Fill=Previous’,默认Fill=Blank |
日期排序(可选) | Order | String | 升序:A/ 降序:D,最近日期在先 | |
交易日历(可选) | TradingCalendar | String | 选择不同交易所所在国家地区日历 | 范例1:’ TradingCalendar =SSE’,默认TradingCalendar =SSE;SSE表示上交所,SZSE表示深圳证券交易所,CFFE表示中金所…… |
输出币种(可选) | Currency | String | 使用货币设置: ORIGINAL:原始货币/HKD:港币/USD:美元/CNY:人民币 | 范例1:’Currency =Original’,默认Currency =Original |
关于指标参数的详细说明见 7.指标常见参数说明
输出内容 | 说明 | |
---|---|---|
错误ID | ErrorCode | 返回值为0 ,则表示代码运行正常。若为其他则需查找原因 |
数据列表 | Data | 函数读取的数据存到此列表中,比如:读取000592.SZ 的close,open指标从’2017-05-08’到’2017-05-18’区间的数据.Data=[[5.12,5.16,5.02,4.9,4.91,5.13,5.35,5.42,5.32],[5.3,5.12,5.17,4.98,4.94,4.93,5.1,5.4,5.4]] |
证券代码列表 | Codes | 输入的证券代码列表 .Codes=[000592.SZ] |
字段列表 | Field | 函数输入中请求的字段列表 .Fields=[CLOSE,OPEN] |
时间列表 | Times | 输出时间序列.Times=[20170508,20170509,20170510,20170511,20170512,20170515,20170516,20170517,20170518] |
1 | # 加载相关的包 |
w.start是启动函数
1 | # 例1、 取富士康概念股近七个交易日的每天机构资金流入额 |
1 | pd.DataFrame(buyamt.Data,index=stock,columns=buyamt.Times).T |
1 | # 例2、 任取一只国债010107.SH六月份以来的净值历史行情数据 |
1 | # 例3、 取国内所有农产品主力合约不同日期所对应的具体合约 |
命令用来获取选定证券品种的历史截面数据
命令原型为:data= w.wss(品种代码,指标,可选参数)
WSS函数输入 w.wss(security,fields, option)
Element | Type | Description | ||
---|---|---|---|---|
证券(必选) | Security | String | 获取数据的证券列表 | 范例:’600030.SH,600031.SH 说明:证券列表支持Wind代码及证券转换类工具函数输出的Wind代码结果 |
指标(必选) | Fields | String | 获取数据的指标列表 | 范例1:’CLOSE,HIGH,LOW,OPEN’ 范例2:[‘CLOSE’,’HIGH’,’LOW’,’OPEN’] |
指标参数(可选) | Parameter/Value | String | 提取指标时使用的参数名/指定参数的值 | 范例:’TRADE_DATE=20170601;FUND_DATE=’20161231’ 说明:多指标参数支持在不同引号内分开取值 |
输出币种(可选) | Currency | String | 使用什么货币 ORIGINAL/HKD/USD/CNY | 范例:’Currency =Original’,默认Currency =Original |
关于指标参数的详细说明见 7.指标常见参数说明
1 | # 例4、 取A股纳入MSCI各成分股的基本资料信息 |
1 | # 例5、 取截止日期 上海证券交易所 发行的国债 基本资料 |
1 | # 例6、 取被动指数型基金最新业绩排名 |
1 | #按今年以来总回报排序 |
1 | first_fund=list(returns_sort.index.values) |
1 | from WindCharts import * |
命令用来获取选定证券品种的当天实时指标数据,数据可以一次性请求,也可以通过订阅的方式获取
命令原型为: data=w.wsq(品种代码,指标,可选参数,回调函数)
函数名: w.wsq(security,fields,func = None)
Element | Type | Description | ||
---|---|---|---|---|
证券(必选) | Security | String | 获取数据的证券列表 | 范例:’600030.SH’说明:实时行情所支持品种较多,基本上终端中有的行情接口中皆可取得 |
指标(必选) | Fields | String | 获取数据的指标列表 | 范例:’rt_open,rt_high,rt_last’ |
回调函数(可选) | Func | 指定回测函数 | 范例:’ func=w.demoCallback’ |
返回选定品种的实时数据,支持一次请求和订阅两种方式。
1 | #例7. 获取沪股通最新一笔的行情数据 |
1 | #任意订阅一只股票的最新行情 |
1 | #订阅行情后当交易所发送新的行情数据时,这里就会推送 |
命令用来获取数据集信息,包括板块成分、指数成分、ETF申赎成分信息、分级基金明细、融资标的、融券标的、融资融券担保品、回购担保品、停牌股票、复牌股票、分红送转等
参数设置为起止日期、板块名称等,不同的报表有不同的参数设置
命令原型为: data=w.wset(数据集名称,可选参数)
函数名: w.wset(view,options),返回股票,基金,债券,商品等专题统计报表的数据。
Element | Type | Description | ||
---|---|---|---|---|
数据集(必选) | view | String | 提取数据集的VIEW名 | 范例1:’SectorConstituent’ |
View参数(可选) | Parameter/Value | String | 提取指标时使用的参数名/指定参数的值 | 范例1:’date=20130531’; |
板块列表(可选) | sectorid | String | 获取数据的板块ID | 范例1: ‘sector=全部A股’ 范例2:’sectorid=a001010100000000’ |
字段列表(可选) | Field | String | 获取字段列表的数据 | 范例1:’field=wind_code,i_weight’ |
基本获取某些板块的数据方法在上面的函数介绍中已经涉及了,这里就不再赘述
1 | #例8、 获取申万一级行业的成分股 |
1 | # 下面分别取各行业指数的成分股 |
1 | #例9、 获取A股纳入MSCI成分股的2017年报的股票分红实施情况 |
1 | #例10、 沪深交易所期权列表 |
命令用来获取一个时间区间内的某种规则下的日期序列。
函数名:TDays(startDate,endDate,[Optional argument])
Element | Type | Description | ||
---|---|---|---|---|
起始日期(必选) | StartDate | String | 时间序列的起始日期 | 范例1:”2015-01-01”,支持日期宏 |
截止日期(必选) | EndDate | String | 时间序列的截止日期,置空取当前最新日期 | 范例1:”2015-06-30”,支持日期宏 |
日期类型(可选) | Days | String | 所有工作日:Weekdays,所有日历日:Alldays,排除所有非交易日:Trading | 范例:’Days=Trading’,默认Days=Trading |
变频参数(可选) | Period | String | 每天一值:D, 每周一值:W,每月一值M:,每季度一值:Q ,每半年一值:S ,每年一值:Y | 范例:’Period=D’ |
交易日历(可选) | TradingCalendar | String | TradingCalendar默认为上海证券交易所,当DAYS为日历日的时候,这个参数不起作用,只有当DAYS为交易日的时候,这个参数才起作用,默认“TradingCalendar=SSE”(上海证券交易所) |
1 | # 例11 取上交所2018年以来的交易日期序列,交易所为空默认为上交所 |
命令用来获取基于某个基准时间前推(<0) 或者后推(="">0)指定天数的日期。0)>
命令原型为:data=w.tdaysoffset(偏移值,基准时间,可选参数)
函数名:TDaysOffset(offset, refDate, [Optional argument])
Element | Type | Description | ||
---|---|---|---|---|
参考日期 | refDate | String | 参照日期 | 范例1:”2015-01-01”,支持日期宏 |
日期类型(可选) | Days | String | 所有工作日:Weekdays,所有日历日:Alldays,排除所有非交易日:Trading | 范例:’Days=Trading’,默认Days=Trading |
变频参数(可选) | Period | String | 每天一值:D, 每周一值:W,每月一值M:,每季度一值:Q ,每半年一值:S ,每年一值:Y | 范例:’Period=D’,默认Period=D |
交易日历(可选) | TradingCalendar | String | TradingCalendar默认为上海证券交易所,当DAYS为日历日的时候,这个参数不起作用,只有当DAYS为交易日的时候,这个参数才起作用。默认“TradingCalendar=SSE”(上海证券交易所) | |
偏移量(可选) | Offset | 偏移参数 | 偏移参数,为整数,>0后推,<0前推,默认为0 |
1 | # 例12 取从今天往前推10个月的日历日 |
命令用来获取两个时间区间内的某种规则下的日期序列个数
命令原型为:data= w.tdayscount(开始时间,结束时间,可选参数)
函数名:TDaysCount(startDate,endDate, [Optional argument])
Element | Type | Description | ||
---|---|---|---|---|
起始日期(必选) | StartDate | String | 时间序列的起始日期 | 范例1:”2017-01-01”,支持日期宏 |
截止日期 | EndDate | String | 时间序列的截止日期,置空取当前最新日期 | 范例1:”2017-06-30”,支持日期宏 |
日期类型(可选) | Days | String | 所有工作日:Weekdays,所有日历日:Alldays,排除所有非交易日:Trading | 范例:’Days=Trading’,默认Days=Trading |
交易日历(可选) | TradingCalendar | String | TradingCalendar默认为上海证券交易所,当DAYS为日历日的时候,这个参数不起作用,只有当DAYS为交易日的时候,这个参数才起作用,默认“TradingCalendar=SSE”(上海证券交易所) |
1 | # 例13 统计2017年交易日天数 |
支持相对日期表达方式,相对日期周期包括:交易日TD、日历日:D、日历周:W、日历月:M、日历季:Q、日历半年:S、日历年:Y。
相关说明:
1、以’-’代表前推,数字代表N个周期,只支持整数;后推没有负号,比如’-5D’表示从当前最新日期前推5个日历日;
2、截止日期若为’’空值,取系统当前日期;
3、可对日期宏进行加减运算,比如’ED-10d’。
举例:
1、起始日期为1个月前,截至日期为最新 StartDate=’-1M’,EndDate=’’
2、起始日期为前推10个交易日,截至日期为前推5个交易日 StartDate=’-10TD’,EndDate=’-5TD’
宏名称 | 截止日期 | 开始日期 | 去年一季 | 去年二季 | 去年三季 | 去年年报 | 今年一季 | 今年二季 | 今年三季 | 最新一期 | 本年初 | 下半年初 | 本月初 | 本周一 | 上周末 | 上月末 | 上半年末 | 上年末 | 上市首日 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
宏助记符 | ED | SD | LQ1 | LQ2 | LQ3 | LYR | RQ1 | RQ2 | RQ3 | MRQ | RYF | RHYF | RMF | RWF | LWE | LME | LHYE | LYE | IPO |
1 | #例14 用日期宏IPO的示例 |
关于WSD/WSS中常见指标参数的举例说明
element | description | value | Indicator examples | demo | |
---|---|---|---|---|---|
tradeDate | 交易日期 | 自填 | 开盘价 | ‘tradeDate=20180618’ | |
startDate | 开始日期 | 自填 | 区间开盘价 | ‘startDate=20180618’ | |
endDate | 截止日期 | 自填 | 区间开盘价 | ‘endDate=20180618’ | |
adjDate | 复权基期 | 自填 | 收盘价(支持定点复权) | ‘adjDate=20180618’ | |
rptDate | 报告期 | 自填 | 净利润 | ‘rptDate=20171231’ | |
priceAdj | 复权方式 | 不复权/前复权/后复权/定点复权 | 收盘价 | ‘priceAdj=U’ | |
cycle | 周期 | 日/月/…… | 成交量 | ‘cycle=D’ | |
bondPriceType | 债券价格类型 | 全价/净价/收益率/市价 | 涨跌幅(债券) | ‘bondPriceType=2’ | |
currencyType | 币种 | 原始币种/人民币/美元…… | 未平仓卖空金额 | ‘currencyType=Cur=CNY’ | |
ndays | 天数(用负号表示前推) | 自填 | N日涨跌幅 | ‘ndays=-5’ | |
rptType | 报表类型 | 合并报表/母公司报表…… | 杠杆率 | ‘rptType=1’ | |
dataType | 数据类型 | 本外币/本币/…… | 贷款利息收入-短期 | ‘dataType=2’ | |
traderType | 类型 | 机构/大户/散户…… | 流入单数 | ‘traderType=1’ | |
exchangeType | 交易所 | 上海/深圳/银行间…… | 跨市场代码 | ‘exchangeType=SSE’ | |
index | 所属指数 | 上证50指数/上证180指数/沪深300指数…… | 是否属于重要指数成份 | ‘index=1’ | |
unit | 单位 | 元、股、份、张、户 | 总市值、注册资本、持有人持有数量、单一投资者报告期末持有份额合计、股东户数…… | ‘unit=1’ | |
industryType/category/industryStandard | 行业级别/行业标准 | 一级行业/二级行业…… | 全部明细、中信行业/申万行业…… | 所属行业名称(支持历史)、所属恒生行业名称 | ‘industryType=1’,’category=1’,’industryStandard=3’ |
adminType | 行政区划级别 | 省级/地级/县级 | 所属行政区划 | ‘adminType=1’ | |
year | 年度 | 2018/2017/2016…… | 管理层年度薪酬总额 | ‘year=2017’ | |
month | 月份 | 1月/2月/…… | 五星基金占比 | ‘month=2’ | |
order | 大股东排名、名次 | 第一名/第二名/…… | 持有人持有数量、基金经理年限 | ‘order=1’ | |
instituteType | 机构类别 基金公司/证券公司/…… | 配售对象名称 | ‘instituteType=1’ | ||
topNum | 名次 | 第1名/第2名…… | 离职日期 | ‘topNum=1’ | |
shareType | 股本类型 | 流通股本/总股本 | 户均持股比例 | ‘shareType=1’ | |
reportDateType | 报告期 | 第一季度(1-3月)/第二季度(4-6月)…… | 单一投资者报告期末持有份额合计 | ‘reportDateType=1’ | |
Type | 资产池资产级别 | 次级/优先级/次优级 | 各级发行总额 | ‘reportDateType=1’ | |
type | 选择权类型、评级对象类型 | 赎回/回售/……、主体信用评级/不限…… | 行权资金到帐日 | ‘type=Pr’、’type=3’ | |
serial | 第N次提前偿还 | 手动输入数字 | 提前还本比例 | ‘serial=1’ | |
ratingAgency | 评级机构 | 标普/穆迪…… | 债项评级 | ‘ratingAgency=16’ | |
bondTypeIndex | 债券类型 | 短期融资券/中期票据/…… | 历史累计注册额度 | ‘bondTypeIndex=2’ | |
bondType | 债券类型 | 金融债/同业存单…… | 存量债券余额 | ‘bondType=1’ | |
termType | 期限类型 | 1年期内/1-3年期内…… | 存量债券余额(按期限) | termType=2 | |
credibility | 估值类型 | 推荐/不推荐/行权…… | 日终估价全价 | ‘credibility=2’ | |
fundType | 基金分类 | 投资类型(一级分类)/投资类型(二级分类) | 同类基金数量 | ‘fundType=1’ | |
fundRatingAgency | 评级机构 | 1/2…… | 四星基金占比 | ‘fundRatingAgency=2’ | |
chargesType | 收费类型 | 前端/后端 | 认购费率 | ‘chargesType=1’ | |
returnType | 收益率计算方法 | 普通收益率/对数收益率 | 几何平均年化收益率 | ‘returnType=1’ | |
annualized | 是否年化 | 是/否 | 近1周回报 | ‘annualized=0’ | |
netType | 报告期净值数据项 | 过去1个月/过去3个月/…… | 报告期净值增长率 | ‘netType=1’ |
1 | 例16 指标参数说明案例 |
Python如图,万矿的财务数据都是传入报告期,然后返回相应报告期的财务数据;在参数设置中可以直接设置报表类型为合并报表/母公司报表/合并报表(调整)/母公司报表(调整)。
万矿是取每个季度最后一天作为报告期,如取2017年的四个定期报告数据,那报告期设置分别为 :一季报:2017-03-31,半年报:2017-06-30,三季报:2017-09-30,年报:2017-12-31
如果要用wsd取多个报告期的公告数据,则需要将周期设置为季,日期类型设置为日历日(因为有的季度最后一天不是交易日)
财务数据回填说明:
1、比如说某公司A在2018年3月22日公布2017年年报,那在2018年3月22以前取2017年年报数据则为空,A公司公布年报数据后,我们会将公布的数据回填到2017-12-31,用户传入相应的报告期参数即可取出对应的年报数据;
2、如果用户直接取出年报数据做回测则会引入未来数据,所以如果要用财报数据做回测要先按照定期报告披露日期拉平至时间序列再做回测。
下面举例说明如何获取各个报告期的财务数据。
1 | #例 17 用wsd函数取000001.SZ[平安银行]近十年的利润表(合并报表)数据 |
1 | #例18 用wss取上证50的常见财务指标的2017年年报数据 |
(完)
特别鸣谢:我家松鼠
旅行结束,慢慢添加我的心绪啦。有人说旅行是痛并快乐着,我觉得没错。偶尔快乐多,偶尔痛苦多。但是南京不太一样,在这里,我几乎没有痛苦,只有一些遗憾和无穷快乐。
告别我纯白如仙的松鼠,我独自拎着箱子,踏上了这次期待不算太久,但我绝对足够向往的旅行。
直达车不直达,特快车不特快,临客不会真的零客,只有高铁和动车还没有辱没名号。或许正是因为他们的出现,其他的客车才相形见绌,铁路系统从此认知失调。车上从各处来的人,混着不同的口音乱杂开放。我很少坐长时间的硬座,今晚恐怕会是个令人记忆深刻的不眠夜。
我初次独自远行去陌生的城市旅游,但也不算完全陌生,还好有认识的长辈在那里,还有松鼠一起,热心地帮我打点安排,让我才能安心地踏上旅途。看着满满当当的旅途安排,我心中说不上来的复杂。自己曾经标榜“读万卷书,行万里路”,然而真正做到还得靠自己一时冲动,当然也归功于周围人们的帮助。虽然很想细细品味,可是走得地方实在太多,眼花缭乱免不了落入走马观花之流,不知所向,只能到现场慢慢品味,再做取舍了。
听说哥哥做打算往往提前两个月,安排行程,预订酒店,这应该很清醒的旅行状态。很期待父母能策划一场出行,但是他们也碍于时间空间的距离,没法随心所欲。松鼠也在帝都寻了一份差事做,于是只留我一个孤独上路。我首次尝试独自出行,我期待一场奇妙的旅行,但是也在不断为之准备着。我着实向往着南京,以至于定下了行程才察觉天气不如意的问题,但冷暖或许已经无妨,我偶尔意决,今天算一次。
有一件值得在意的事:在火车上,教室里,坐着的时候,感觉自己的位置很清晰。但一站起来就瞬间失去方向感,不知自己身处何方。究其原因,或许是同一个视角看的太久,在升维瞬间失了格吧。
冬雨不寒马蹄疾,一日看尽金陵花。
湿身方嫌寡情雨,酩酊难解太浮夸。
突然吟诗,说明开心。作为一个本意独行这个城市的旅人,没想到获得了异常热情的款待,在一个伯伯的帮助下,我今天辗转了无数景点,尝遍了千万小吃,大饱眼福的同时也大快朵颐,精神和肉体皆是满载而归。
白天刚到南京,漆黑混杂着大雾混沌成一片,南京像个封存老窖中的佳酿,像黑暗中待开启的宝箱,像迎进屋子但披着盖头的新娘,待人一探究竟。逐渐习惯了那些随心所欲的道路规则和巨大显示屏展示的宜人的红绿灯倒计时灯,其实这座城市没有我一开始想象的那么井然,但是在我眼中这般混乱之下,竟也有可以寻找的规则和有序。人人在这里都可以大摇大摆地做天王老子,享受每一刻在路上的时光。我也很享受在这里大街小巷行走的快乐。
我行程飞快。从长江大桥江底隧道,路过狮子山阅江楼,草草翻过毗芦寺,梅园新村,六朝博物馆,再到小九华山,鸡鸣寺和玄武湖,雨中闯入大钟亭,鼓楼和南京大学,夜里的狮子市,夫子庙,穿街走巷雨中狂奔才赶到的1912,没有哪一处突兀多余。这些或自然或历史或现代的风光景色,都已和南京融为一体,俨然成了南京现代生活的一部分。在意想不到的地方,总会突如其来杀出一座山,一座楼,一道水,一院佛。这里充满了人文和自然气息,物华天宝、人杰地灵。这里据说寸土寸金,跨过三五步一条马路突然就是一个崭新的迥然不同的世界,每一步都可能产生新的邂逅,这种未知的刺激令我着迷。南京也多美女,平日里原子弹炸不到一个,南京一颗手榴弹能炸一片,松鼠还嫉妒。
城市的诗情画意,还有它不为人知的小心思,我都有所理解。不仅仅是小面包车上与伯伯一字一句的对谈和学习,更多的是一种气质:城市的气质、人的气质、全部自然总体总和的气质。我听到了地道的南京话,插科打诨,无比惬意,虽然不习惯其中个中洒脱粗鄙,但是想到儒雅如我这个三秦猛男也常常口吐芬芳当街骂娘的事实,我也笑着理解了这种调侃性质的口癖。南京话总有种火药味,还是不要太较真的好。
明天还要早起,今天走着玩几万步,像是玩着走。白天五点到站还没睡够觉,实在困累之至,加上本应该成为东道主的松鼠也沉迷游戏,不搭理我,夜里雨中骑车狂奔尽兴而归才发觉丢了伞,浑身湿透地回了宾馆。大欢喜背后总是一个个小失落。松鼠也质疑我、嗔责我,虽然看起来像是口是心非,有了些南京人互怼的味道,但依然令我十分难受,可是又不想反驳什么,坏了我的期待和焦渴就算了,让松鼠批评一下舒服舒服也无妨。且熄一熄我这团让我膨胀到无所适从的火焰吧。
明天还有明天的旅途,眼皮打架,不说了。
早起我就感受到了南京别样的一面,至少在没有暖气这一点上,就宣告了它是一座冰冷的城市。融入和接受恐怕很难,我想只有熬得住寒冷、耐得住性子,才能和这座城市打成一片,才更加接近这座城市的灵魂所在。我还是在寻找神奇的路上,从未停止。我也住过很久没有暖气的房子,全靠电暖气续命,每天起床前一定要把冰凉的衣服在暖气上烤一下,起床才不至于那么煎熬。
今天的目标颇多,本来只与昨天持平,结果今天伯伯突然有事,无力再送我。我已经心怀感激,哪敢多做要求,于是今天的路成全了昨日口中奢望的“独行”。就结果来看,我还是奢望有车接车送只管参观的旅途。不过今天去的景点都是需要用双脚踏踏实实丈量的,倒也完全没有投机取巧的空间,要车也无用。于是就诞生了这近乎4万的超越极限的旅程,从表面看来有车的旅行效率可以提高一倍。
气度雍容的美龄宫开启了这段钟山之旅,皇气浩瀚的明孝陵,排衙端庄的颜真卿碑林,肃穆清白中山陵,绝景入云的灵谷塔,还有一些情趣无限的小地方:三绝碑、音乐台、无梁殿、四方城。虽然走马观花,对历史由来也一知半解,但我总算是将那奇人奇景刻进脑海,待之后再品味。没顾上吃饭,斩了根烤肠,又转头向环环相扣的瞻园,途经深街老巷的老门东,又仰止中华门,行止雨花台。这么多还不够我体验,晚上又补足了第一天遗憾的玄武湖,还去了夜幕下的三山街熙南里,新街口商圈,论充实,这一天的容量比起考研的长途作战有过之无不及,我也好久没有来过这样的高强度旅行了,导致我写文章这会儿只能像一张煎饼一样在旅店中摊开来任人宰割,甚至因为太累了,害怕泡澡堂子散了紧绷的神,最后没去。
一路上都是人文景观,而绝美的风景并非很多,触动内心的更少。南京还在雨中,紫金山烟雾蒸腾。雨让天地模糊了界限,这混沌让我有些烦躁,可是微雨再不增添更多烦恼之上,把凝重和游人从景区一同抹去,留给我肆意想象和发挥的空间。在过年前的下雨的工作日游览南京,或许这体验只有现在才可能得到。这个季节的钟山树林红褐色与深绿色交融,错综的枝桠遮天蔽日,只有登高临台才有如此疏朗的开阔视野。但真正居高的时刻,我又没了高屋建瓴的气势,变得不知所向,无所适从。年轻时候,爱登高楼,待我再成熟一些,怕是就失去登楼心劲了。现在总觉得高楼如家乡这般,珍惜之处想去可是又不敢去,顺心和忧愁总是相伴而生,这就是爱吧。
颜真卿的碑林和刘勰文心雕龙纪念馆在钟山一角,本就与帝王伟人陵寝格格不入,但更出格的是一座无人的小园子还夹在两个建筑物之间,毫无征兆地撞出来,园虽小,但阁廊水木应有尽有,明明修在一个平面上,竟也有小曲折和错落的层次感,可能南京的园子有难名且优雅的修葺之道吧。我很喜欢这个没有名字的园子,更多是情景之喜,先前的凝重的地气在此行不通,进去出来竟然变成一园的清淡芳香,有如解油化腻的饭后小汤,沁人心脾。
其实我对园林毫无体验,来之前甚至只有一些北方园子的印象。观赏中若觉得某处入眼甚是契合我美感,凭空而爱,毫无理由,因此不好意思多说。看了瞻园,层层叠叠,钦佩和沉浸感无以复加,惬意凉亭。只是零落季节的园子,竟都有此等魅力,引得我反复穿行,寻找角度,左看右看,去留频繁。园子的窗门给出无限空间、亭廊自如穿梭带给人想置身其中的欲望、山水花草有情有据铺陈摆放十分讲究,惊为天作。雕梁画栋、星星灯火,与这季节的残花败柳,相映成趣,爱至深,盖我已成园内人。但这种“同化”有例外,我在园中发现了一只猫,在雨中漫步,愁眉苦脸,仿佛并不开心。我笨拙地学了几声猫叫,企图诱骗,反倒逼它使出浑身解数,飞檐走壁消失在楼台间。或许我不会骗人,更不会骗猫。我的体会就连我自己都不能完全把握,异想天开地企图把喜悦传递给猫猫,简直是乐昏了头。不过有趣的是,察觉到自己似乎“很快乐”的我自己,就接着认真地快乐起来。
猫猫是有灵气的,每一只猫都有,于是我喜欢看猫。人不一定,但也有灵气。昨天在小九华山的一条幽径中,一直黑猫欣然路过,丝毫不在意一旁赶路的我,瞬间隐匿,留我略略遗憾。今天晚上玄武湖的湖中小洲上也遇见了小黑猫,自顾自躲雨,看我几眼,又故作高冷,转身翻窗离去,似乎还被窗子卡了几秒钟。我本来想帮它,但觉得猫猫灵性至此,我一介庸人又能改变什么呢,或许不论它能不能穿过那条窗缝,我都不该出手才是。
可是,猫猫不改本性,我的认知上限却是容易被改变的。神道漫长,上山路上遇见一位高人,内力颇深,一曲《青藏高原》响彻整条路,我们身形交错瞬间,竟有种被浩然之气浇透打穿的感觉。我很少为了看人而回头,但这次如同一定要确认什么似的回了头。我也趁没人唱了两句,除了遭了报应绊了一跤差点滑倒,好像没有什么场地的加成。我猜想可能有两个原因:一是我不该放浪形骸,陕西人有时邪,说曹操曹操就到,陵寝高歌还是不好;二是我的唱歌内力太稀薄,遇见高手甚至会反噬自己,平时多是献丑。
其实,这一趟转悠下来,我也不知自己真正喜欢上了南京的什么,也不知道自己不喜欢什么。心中也像下了一场雨,界线模糊了。但明显的一点是,原本我不喜欢或者丝毫不感兴趣的东西,我开始尝试了,甚至会用心找它的特点。就比如我把主打甜味的梅花糕,形容为嬉笑怒骂,我自以为完全合理。粘和脆,苦和甜,硬和软,彩和白,一对对矛盾交织,共同构成了这一口梅花糕。或许真应该用“嬉笑怒骂”来形容,着实难忘。松鼠觉得不该把外壳做糊掉,糊掉生出多余苦味,可是那样的甜可能也单调,而且恐怕也只有高明的店家才能做到,并非是人们常常吃到的那种,少了一种地气吧。
吃的事之后专门写一篇。很晚了,休息。明天还要拼一拼,不过早上可以慢悠悠吃个早茶。人总是忙碌的,就拿我来说,担心的事有很多,我还悬着一颗梦想抢改签票的心,现在还担心松鼠的身体,坏的发展都是受罪。我不想松鼠受罪,我也不想受罪,可是有时候确实得为了所爱的什么拼一把。人是成长的,有些人走不了那么多路,可是热情驱使着,就会毫无征兆地焕发出超越身体机能的力量。
今天去总统府,天下文枢坊,莫愁湖,奥体公园,还有大屠杀纪念馆,为南京之游收了官。每一处都有独特的感官体验。吃得好,玩得好,虽然走马观花,但也算肤浅到极致,极尽三天之能事,算是不虚此行。每每看到它新的面貌,有时是市井平常,有时是青春自由,有时是工作辛劳,不管什么样子,当我更加了解南京时,就又多几分喜爱。
从来没有高强度这么拼命地走过路,我很少如此用痛感苦感来强记某些旅程,但若旅途中带着一些苦痛和不顺利,那此行也会愈加刻骨。整条路也多亏只有我一人,我可以随心所欲走南闯北,当然也因为计划不周耽误了很多时间。所幸,南京是个交通便利的城市,用不着我在路上费劲,就能轻松地兜转访寻。限制自己的只有昨天透支的腿,让今天确实无力实现出格的想法,脑子都不灵光了许多。若说前两天在旅行,今天却像是一种修炼和补救错误。
这次出游,没做什么攻略,没写什么太长的安排,每次游览景点,也只是按照一个深度随心逛下去。也不知道有什么美食和根源,只好误打误撞,有时现场搜索,或蹭蹭别团的导游。狼狈不堪。今天也萌发了一种无力感,起因于我偶然听到总统府中的一位导游讲起府门前八字台阶和某“森”楼奥秘,我才察觉自己的充实只是肤浅的充实,有些事有些来由根本不是我一个人能查到或者体会到的。除了对先前的自满自惭形秽,我也更加喜爱南京这座城。
其实和松鼠聊过,但是免不了再提一嘴。在我眼里,南京无疑是一座“城”,有声有色,有古有今。但我不曾了解南京的那部分,却在我心底呼之欲出,而那一部分更像是一座“市”。人们日出而作日落而息,每个人都寻着一份事儿做。我独潇洒,逆流而动,这些天还真无人与我争,可能是因为天寒又下雨,还赶上年前大家都忙碌,基本都是独享美景,宛如包场。吃东西也一样,我作为半个吃货,常苦于选择困难,很少能搏得最好吃的头彩。但尽管这样,我还是被南京本地美食所征服,足见南京美食同文化一样博大精深。
其实,我总在渴望从不同的角度认识南京,渴望从最真实又最深刻的一面认识人事物。这次走马观花,我抱着能触碰这座城市除了历史以外的全部表象的另类的渴望,我想融入这里早上的忙碌生活,想度过不眠的灯火秦淮,体会拥堵和顺畅,看遍美食与美人,我喜欢更平常的南京,而非更“南京”的南京。或许这些微妙的差别只有等我再咀嚼时,才会体会。不过我想,这次快速游览,大概并非毫无意义,对我来说这算是已经铺好纸笔,只待我闲暇时再去看两眼,便能描画挥洒了。
说了许多大话空话客套话,可是没学会最想了解的南京话。当然,就算学会,我也不敢在老南京们面前班门弄斧,最多调侃一下松鼠。但是一点可以肯定,南京人的普通话不怎么好,至少这一路我时刻注意着,和我对话的人,讲普通话的不超过一手之数,大多数普通市民都会带着点南京的口音。通常,本地人对本地方言都有些特别的情节,我听了几天,虽然有些难名的词根和语缀记了几个,南京话虽然听着像北方话,却抛去了很多太刚坚的词,剩下一些令我摸不着头脑的婉转语调,以及一种鞭辟入里的直爽,和一种说完就抛的洒脱。出口成脏是常事,有人确实喜欢拿腔拿调还吹胡子瞪眼,只是听者不必钻牛角尖,应该用脏字还一口回去才是正解。看他们聊得火热,我感叹语言排外的严重,初来乍到完全假装不了本地人,可能也因此没能混进想看的学校。不管如何,南京话稍稍让我有些羡慕。
想到今天又去夫子庙,这次奔着几个文化展馆去的,个中展馆确实值得玩味。再回味前两次来这里,总觉得看到的秦淮灯火只是一种虚假的繁华。有趣的小玩意琳琅满目,可我不屑一顾;当地独特的美食飘香穿巷,可我吃过后便无心驻足。然而我偏爱这里的砖瓷碑匾,飞檐叠壁,亭台廊阁,我深醉这里的兵戈祸中,天下文枢,古都气韵。这一城氤氲于书香食色,融化于绵山长水,烙刻于时代纵横,这样的城,只南京一座。
你们又要说,去过南京就开始吹南京,是不是收了钱奉了旨,屁颠屁颠地傻乐呵。南京是个普通城市,也有着普通的一面,那是每个城市都有的市井市侩的一面。主干道上还在修建便民交通,拥堵是不可避免的,这座城市还在发展之中,还在成长之中,还好相见不晚,未来可期。
我眼中的平凡事也多,有取纸巾铲屎的路人老奶奶,有骂骂咧咧却又热心的公交司机,有高唱分手快乐的醉酒小情侣当街大戏,有书包加身的名校学子匆忙赶路尽显惫色,有早点摊、总统府、瞻园、玄武湖和小九华数不清的悠闲猫猫,有不喜欢戴手表手机的晨练大爷和急着下班地铁引导员,有追出五十米把落下东西送还的餐厅服务员,有无心逛街还等在商场门口的寂寞小男友,有快下班了还特意为我重启设施的景点管理员,有好心过问冷暖的酒店前台,有不给好脸色的公事公办学校门卫,有飞行在街巷的电动车手,有遛狗带娃不栓绳的小阿姨,有酒吧街用生命力高谈阔论的落魄青年,有串摊指点附近美食攻略的温柔小哥……明明只有三天,我遇见的人们比我想象中的要更多更丰富。不管事由如何,这一切的人一起组成了我眼中的南京市民,正是他们生活在这片城市中,带给这座城市无限生命力。刷新了我对南方人和南京人的认识。
你若问他们怎么看我,我觉得像是在看一只外地来的金丝猴:操着听不出籍贯的口音和生得一副独特的面貌身材,带着目中无人怼天怼地的气魄,三步并一步飞快穿行的孤独旅人。外地人来南京,南京人都无比自豪,开始话六朝古都,十洲云水。作为半个西安人,我对历史有莫名的感情,可惜这次没去成最为推崇的南京博物院,是最大的遗憾了,但也不算遗憾,反正走马观花看博物馆,那才是真的浪费时间。
之后再专门写点美食吧,在此不做赘述。我喜欢南京。
(未完待续,还有春运和美食没写qwq)
15晨
桂花糖芋苗
赤豆元宵
鸡汁汤包
鸡汁回卤干
15午
慈悲素鹅面
沉鱼落雁(松鼠素鱼)
煮干丝
15晚
清炖狮子头
江南水乡一桶仙
蟹黄汤包
香干马兰头
清炒芦蒿
肥肠臭豆腐砂锅
千层油糕
桂花拉糕
糯米糖藕
16日晨
油条
烧麦
南瓜红枣粥
16晚
张三生煎+垃圾锅贴
梅花糕
17早
酥饼
鸭血粉丝汤
17午
喜妞炸串 鸭肠
晨祥记牛肉锅贴
桂花双皮奶
酱汁鸭肉卷
奇异果青麦汁
芋圆bobo鲜奶茶
17晚
香辣牛肉锅盔
酱汁烤鸭