pythom 输入一个4位整数判断输入是否为整数里面包含多少个偶数,多少个奇数

pythom函数
# 什么是函数? 就是将一段代码写在一起,如果需要使用,直接用即可
# 封装了一段功能代码的代码块,这段代码可以被多次使用
# def define:定义
# 函数名称命名规范: 单词全部小写,多个单词之间用_下划线连接,
# 函数名要和实际实现的功能有关系,做到见名知意
# def 函数名(参数列表):
函数功能代码
def eat():
print('先去买菜')
print('洗菜')
print('切菜')
print('炒菜')
print('吃饭')
print('刷碗')
# 函数想要执行,必须要调用
# 带有1个参数的函数.多个参数之间用,逗号隔开即可
# 参数:执行函数所需要的一些数据
# 例如去商店买水,买水就可以看作是一个功能函数
# 参数的第一种类型,必须要参数,必须要传递的参数
def buy_water(money,name):
print('你给了%s块钱,你要买%s'%(money, name))
# 调用带有一个参数的函数
buy_water(10,'农夫山泉')
# 关键字参数
这样就不必按照参数定义时顺序进行传递了
buy_water(name='农夫山泉',money=10)
# 缺省参数
参数不是必须要传递的,参数会有一个默认值,如果调用时没有传递
# 该参数,那么参数就是默认值,如果传递过来了,参数就是传递过来的这个值
def sleep(time = 0):
print('你睡了%s小时'%time)
没有更多推荐了,&figure&&img src=&https://pic2.zhimg.com/v2-33af78a9f3ef1cba8fdc525a5769cb61_b.jpg& data-rawwidth=&675& data-rawheight=&429& class=&origin_image zh-lightbox-thumb& width=&675& data-original=&https://pic2.zhimg.com/v2-33af78a9f3ef1cba8fdc525a5769cb61_r.jpg&&&/figure&&h2&&b&1 引言&/b&&/h2&&p&&b&回测(backtesting)是任何一个量化投资策略上线之前必须要经过的一道流程;&/b&它是量化投资策略和其他类型投资(如主动投资)最大的区别之一。回测将投资策略应用到历史数据中并考察它的表现。具体的,回测有如下作用:&/p&&ol&&li&&b&验证投资策略是否有效:&/b&我们很容易从学术文献、投资书籍、券商报告、以及互联网得到很多交易策略的想法。回测可以快速验证一个想法是否有效(净值曲线至少得是向上的)。&/li&&li&&b&对策略进行参数调优:&/b&当一个交易策略有效的时候,回测让我们优化该策略的参数。比如,一个均线策略,我们使用 20 天的均线还是 30 天的均线?当然,在优化的过程中,我们需要非常小心、避免过拟合的问题。&/li&&li&&b&给出策略在未来表现的预期:&/b&任何一个靠谱的量化投资策略都是基于对市场行为的某种假设;该假设背后反映的是某种特定的业务逻辑。量化策略假设市场的这种行为在未来会重复。因此,策略在回测中的表现将对其未来上线后的表现(如收益率、最大回撤、交易费用等)起到参照作用。&/li&&/ol&&p&&b&正确、科学的回测对构建量化投资策略至关重要。&/b&然而,回测中又有很多“坑”。如果不能正确的认识并避免它们,回测的结果很可能对我们产生误导,错误的回测更是会导致实盘中的(严重)亏损。今天我们就来和各位小伙伴聊聊我们是怎么理解回测的。本文将从下面几个角度论述:&/p&&ol&&li&超参数;&/li&&li&训练集、测试集之纠结;&/li&&li&各种偏差;&/li&&li&不可忽视的交易费用;&/li&&li&回测并非无所不能。&/li&&/ol&&h2&&b&2 超参数&/b&&/h2&&p&&b&超参数(hyperparameters)是参数的参数,是源自机器学习中的一个概念。&/b&在机器学习中,超参数是在模型开始学习之前需要人为设置值的参数,而不是通过训练得到的模型参数。&/p&&p&在构建量化投资策略时,虽然不一定会用到机器学习算法,但是超参数的概念依然非常重要。比如在技术分析策略中,我们是使用通道突破还是均线交叉?是使用 ADX 指标还是 SO 指标来测量动量的大小?这些需要人为决定的参数就属于超参数。一旦确认具体使用的指标后,至于计算指标的参数(比如计算通道和均线需要的时间窗口)就可以通过回测来优化。&/p&&p&另一个超参数就是回测期的时间窗口到底选多久。这是任何量化投资策略都必须面对的问题。比如我们测试一个量化选股因子,那回测期是从 2000 年开始,还是 2005 年开始,还是 2010 年开始等,这是事先必须确定的。&/p&&p&随着机器学习算法在量化投资领域的应用愈发普及,我们在使用它们时也势必面临着同样的问题。比如在集成学习中(如随机森林),决策树的数量和深度是多少;在 K-means 算法中簇数的个数;在 SVM 算法中核函数应该选哪个。这些都是超参数的范畴。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-7fec7c696c3071dda342c95db40e5b9f_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&521& data-rawheight=&498& class=&origin_image zh-lightbox-thumb& width=&521& data-original=&https://pic1.zhimg.com/v2-7fec7c696c3071dda342c95db40e5b9f_r.jpg&&&/figure&&p&对于典型的机器学习问题,超参数也是需要优化的,以此提高机器学习算法在样本外的性能和效果。&b&对于量化投资策略来说,超参数的调优除了从数学角度(如策略的 Profit/Loss 曲线)考量外,还需要考虑超参数本身是否具有清晰的业务含义以及是否适合策略的使用者。&/b&这里举两个例子。&/p&&ul&&li&&b&例子一:&/b&假设使用 SVM 来预测股票收益率,输入的特征可以是各种量价数据和基本面数据,输出为涨跌的预测。如何选择 SVM 的核函数就是一个超参数问题。如果选择了非线性的核函数,它会把特征向量进行非线性变换、映射到更高维的空间。这当然有助于提升预测的准确性,但是人们往往难以从金融投资角度解释特征向量的非线性变换,因此这样的策略就不折不扣的成为了黑箱。&/li&&li&&b&例子二:&/b&趋势追踪策略既可以使用均线交叉也可以使用通道突破。在它们之间的选择就是超参数的问题。然而,这两类策略的风险收益比显著不同:均线类策略交易次数多、胜率低、平均盈利和平均亏损都较低;反观通道突破策略,它交易次数少、胜率高、平均盈利和平均亏损都较高。不同的策略背后折射的是完全不同的“投资哲学”,投资者需要根据自己的风险偏好选择。如果仅从数学角度考虑选取了一个并非让自己十分舒服的交易策略,那么在实盘中注定会失败。&/li&&/ul&&p&&b&除了上述这些明确的超参数外,有一些因素需在构建策略之前确定,但是在回测中我们会根据策略的表现对它们进行反馈。这类因素介于超参数和策略参数之间。&/b&比如在资产配置策略中选择哪些资产放入资产池;又如在量化多因子选股策略中,使用哪些风格因子进行选股。&/p&&p&在资产配置中,我们必须事先决定一个资产池(asset pool),然后构建策略。但是随着回测的深入,如果发现某类资产的加入对于策略的表现没有正贡献,我们便会从资产池中剔除掉该资产。类似的,在多因子策略中,我们事先拟定了一些具有超额收益的风格因子,但如果回测发现一些因子造成投资组合的可交易性太差(比如换手率太高,或者选出来的都是流动性差的股票),那么也会剔除掉这些因子。像资产池、因子库这类“参数”,都需要在构建策略前确定,但是策略的表现又会指导我们进一步优化它们。&/p&&h2&&b&3 训练集、测试集之纠结&/b&&/h2&&p&任何一个介绍量化策略或交易系统的文献都会强调在构建策略时,一定要把历史数据分成训练集和测试集,在训练集上优化策略的参数,在测试集上考察策略的表现。(我曾经看过一本英文的关于交易系统构建的书,洋洋洒洒 300 来页,基本上唯一的核心思想就是要有测试集……)这个论调固然没有任何问题,但是交易数据中的一些特点使得是否区分训练集和测试集有时变得很纠结。&/p&&p&&b&区分训练集和测试集的初衷是避免参数在样本内过拟合,从而导致策略在样本外(上线后)的表现和其在样本内的表现大相径庭。&/b&在一个典型的通过训练集和测试集来对参数调优的过程中,通常的做法是调整算法迭代的次数或者惩罚系数之类的。随着参数的调优,训练集上的误差单调递减,等测试集上的误差开始提升,就停止调优(下图)。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-c9cef822b071c4c17206bb_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&709& data-rawheight=&563& class=&origin_image zh-lightbox-thumb& width=&709& data-original=&https://pic4.zhimg.com/v2-c9cef822b071c4c17206bb_r.jpg&&&/figure&&p&&b&一个经科学回测过的策略应该能够捕捉训练集和测试集数据表现出来的某种市场共性。&/b&任何交易数据都包含一定的噪声成分,使用测试集是为了避免模型(策略)过度错误的考虑训练集内样本包含的噪声,更好的发现共性。然而,&b&交易数据的严重匮乏和市场环境的频繁变化使得上述诉求成为一个遥不可及的美好愿景。&/b&&/p&&p&我们说交易数据严重匮乏是就数据的&b&独立性&/b&以及数据的&b&可交易特征&/b&而言的。&b&从这两点而言,在构建策略时是否一定要划分训练集和测试集、以及如何划分就需要非常谨慎。&/b&&/p&&p&由于市场流动性环境和宏观经济的变化,以及波动率聚类等特点,投资品价格数据的短期相关性比较高,而在不同的历史时期往往反应出不同的特点。&b&如果训练集和测试集的数据代表了不同的市场状态,那么它们之间的交易数据就没有多少共性可言。&/b&相对于训练集来说,测试集中的数据满足训练集中假设的样本太少,以此来优化策略势必忽视了测试集数据中传达出的新的市场环境。对于一个策略来说,我们都不希望忽略市场的任何特征(除了噪音)。&/p&&p&在可交易特征方面,满足一个策略假设的样本其实很少。举个极端的例子,比如 A 股中追踪大牛市的趋势类策略。在过去 20 年中,也仅有三波牛市,而且它们表现出来的市场特征均不相同(比如以 2007 年大牛市构建的趋势追踪策略在 2015 年大牛市中的逃顶效果并不好)。在这种情况下,如果还把数据分成训练集和测试集会怎样呢?&b&我们一定会把策略在测试集中的体现出来的新市场环境反馈到训练过程中,这已经违背了分训练集和测试集调参的本意;这等价于我们在整个历史数据中对策略的参数调优了。&/b&比如我们使用训练集构建一个策略,但是在测试集上遇到股灾了,为了对付它,就强行加上一个下跌 XX% 就止损的条件。这其实不是在对原始策略的参数调优,而是针对测试集的样本去改变策略的初衷!如此来“优化”策略,又还有什么训练集、测试集之分?当然,这么做的出发点是好的 —— 让策略尽可能的适应不同的市场环境,在真正的样本外(上线后)的适应性更强。&/p&&p&对于是否一定要分训练集和测试集,我的看法是&b&这种防止样本内过拟合的思想是绝对可取的,但如何执行应该试策略而定。&/b&比如对于用机器学习算法来预测短期收益率的策略,使用训练集和测试集就是恰当的。对于量化因子选股策略,这似乎又是个伪命题。众所周知,小市值因子在股灾 2.0 之前一直有效,但从 2016 年初开始,随着价值投资的回归,小市值因子彻底失效。又比如,价值因子(或者说就是“上证 50”因子、“沪深 300”因子)在最近两年有效,但是随着它们估值的提升,谁也不能保证它在未来持续有效。&b&一个经科学回测过的策略必须能够捕捉市场的某种特点。只要这个特点有明确的业务支持而非数据挖掘的产物,那么针对这个特点构建及优化策略参数时,是分别使用训练、测试集,还是使用所有数据来整体优化,这并不重要。&/b&&/p&&h2&&b&4 各种偏差&/b&&/h2&&p&本节介绍回测中的一些常见的偏差,它们包括&b&优化偏差(过拟合)、前视偏差、以及幸存者偏差&/b&。常见的介绍回测的文章的重点就是这些。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-25b7c3bbb3a21b92b71f_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&665& data-rawheight=&288& class=&origin_image zh-lightbox-thumb& width=&665& data-original=&https://pic2.zhimg.com/v2-25b7c3bbb3a21b92b71f_r.jpg&&&/figure&&p&&b&优化偏差(optimization bias)指的是通过不断地增加参数个数,直到策略的收益曲线在回测中表现的非常好。&/b&参数越多,越容易在样本内“处处精准”。这意味着对样本内数据的噪音过度建模,及过拟合。&b&防止优化偏差的方法是减少参数的个数 —— 比如能够使用一个技术指标就不要使用两个 —— 以及增加回测数据。&/b&在这方面,可以从两个维度来增加。一个是使用更长的历史数据,比如一个策略在过去 10 年有效就比在过去 3 年有效更有说服力。但这么做时需要考虑市场状态变换(有可能太久远的历史数据并不满足你策略的假设),因此回测数据也并不是越长越好。第二个维度是考虑不同的投资品。如果策略和一组给定的参数在多个投资品上都有效,它无疑更有说服力。&/p&&p&此外,&b&通过考察参数平原(parameter surface)也可以检查过拟合。&/b&将策略表现的指标(比如收益率或者最大回撤)画成参数的函数就是参数平原。&b&对于一个好的策略,在微调参数取值时,它的表现应该比较稳定,参数平原应该十分光滑。&/b&比如如果最优的均值参数是 20 个交易日,则当我们使用 19 或 21 个交易日计算均值时,策略的表现和 20 日均线的策略不应该差很多。如果策略的参数平原非常不规则,则意味着参数背后没有合理的业务逻辑,而更多的是数据挖掘的结果,这时就要小心。&/p&&p&&b&前视偏差(look-ahead bias)指的是错误的使用了未来数据。&/b&在构建策略时,尽管我们会非常小心,但仍然会时不时的步入前视偏差的陷阱,这往往是编程时的疏忽造成的。举两个我自己的例子。有一次我使用卡尔曼滤波(一种状态空间建模方法)构建了一个预测模型,我很小心的使用截止到每个交易日当日的滚动窗口以避免使用未来数据,结果回测的效果非常好,“好到一看那净值曲线就知道一定是哪里出了问题”。后来经过检查发现,在使用该方法的第一步有一个初始化的过程,在那里传入了所有历史数据,而非截止到每个交易日的数据,由此引入了未来数据。另一次我测试一个技术分析策略,该策略的收益曲线也是非常吸引人。后来发现,程序中使用了最高价和最低价数据,并假设当它们出现时触发交易。这显然是有问题的,因为只有当一个交易周期结束后,才能知道这段时间内的最高价和最低价是多少,而到了那个时候,早已无法按该价格成交。所以,通常在使用最高、最低价时,必须至少使用滞后一期的数据。同样的问题也会发生在使用基本面数据或宏观经济数据的策略中,我们必须对这些数据何时发布非常小心,以免在回测中提前使用这些数据,造成前视偏差。&/p&&p&&b&幸存者偏差(survivorship bias)一般针对选股策略而言,指的是在数据集中错误忽略退市的股票。&/b&在构建选股策略时,我们有时会下意识的仅考虑在整个回测期内都存在(“活着”)的股票,而忽略掉在回测期内退市的股票。这么做会提高提高策略在回测期内的表现,因为那些差的、退市的股票根本就没有被考虑在内。但在实际交易中,我们无法提前预知哪些股票会退市。因此,忽略退市的股票是不恰当的。这也是一种“前视偏差”。&/p&&p&量化投资较主动投资的优势是,它可以摒弃人的情绪,排除人在交易时由各种认知偏差带来的额外的风险(见&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&让你投资亏钱的15个“偏差”&/a&)。在回测中,也只有尽量摒弃各种偏差,才能使它发挥出最大的作用。&/p&&h2&&b&5 不可忽视的交易费用&/b&&/h2&&p&&b&一个回测中赚钱的策略和一个实盘中亏损的策略之间的差别是什么?交易费用!&/b&&/p&&p&&b&交易费用包括手续费(commission)、延迟造成的滑点(slippage)以及流动性造成的冲击成本(market impact)。&/b&其中手续费的估计最简单,券商有固定的手续费比例,只需要在回测中考虑到即可。滑点和冲击成本则很难在回测中准确的反应。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-842b30c9d061ddb91dab95dba3d57317_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1057& data-rawheight=&530& class=&origin_image zh-lightbox-thumb& width=&1057& data-original=&https://pic4.zhimg.com/v2-842b30c9d061ddb91dab95dba3d57317_r.jpg&&&/figure&&p&&b&滑点指的是在交易信号产生到交易实际执行之间价格的(不利)变动造成的额外费用。&/b&这对于中、高频策略额外重要。这些策略捕捉日内转瞬即逝的交易机会,如果回测中策略的表现依赖于按照交易信号产生瞬间的价格来交易,那么这个策略在实盘不会有什么前途。这是因为实盘中难以保证会交易到信号发生瞬间的价格(价格出现后才能计算交易信号,交易信号需要送到券商,然后送到交易所交易,这之间都需要时间)。为此,回测中可以考虑的是按照每个 bar 的收盘价来交易。这个 bar 的频率可以很高,比如分钟级,但一定是按照它的 close 价格来成交,而非在这个 bar 的形成过程中信号触发的瞬时价格。又或者,在回测时考虑信号出现后一段时间内按照 TWAP(时间平均)成交。在这些设定下并考虑适当的滑点,如果该策略还能赚钱,那么在实盘中才有机会。此外,滑点对趋势追踪策略的影响也比较大。对于这类策略,交易信号产生时,价格一定是向着预测的方向移动,这意味着我们必须追价,滑点无法避免。&/p&&p&&b&冲击成本指的是投资品流动性不足带来的额外交易费用。&/b&对于流动性高的投资品,限价指令簿(limit order book,就是我们在交易软件上看到的 level 1 买卖各几档的挂单情况)上,买一和卖一之间的价差(bid-ask spread)很小,且两边的挂单量都足够。交易这类投资品不会带来太大的市场冲击。但如果投资品的流动性不足,那么为了完成交易,策略就势必会 walk the book(在一个方向上“收单”),直到完成我们的交易量需求。这个冲击成本和每笔交易时的具体流动性有关,因此回测中难以准确估计。&/p&&p&鉴于交易成本的重要性,在回测中必须假设合理的交易费率,宁可高估也不应低估。通常的做法是估计几档不同的交易费用,比如每笔交易单边千分之一、千分之三、千分之五甚至更高等。以此感受一下策略在不同费率下的收益和回撤情况。但真实的交易成本仍然需要一段时间的稳定实盘交易来评判。&/p&&p&&b&能否有效的控制交易费用直接关系到一个策略的资金容量。资金量(交易量)越大,完成交易所需的时间越长,而交易成本(及不确定性)随交易时间呈非线性增长。&/b&由于交易量巨大,华尔街的大型金融公司都会针对市场微观结构(即 order book 上买单、卖单出现的 dynamics)来建模,以此构建交易模型(注意是交易模型,而不是投资策略模型),从而尽可能的降低交易成本。&/p&&h2&&b&6 回测并非无所不能&/b&&/h2&&p&科学的回测对于量化投资策略至关重要。但是,&b&回测也并不是万能的。&/b&&/p&&p&&b&一个经过严格回测的策略投放到实盘时最大的敌人是一个人的心理关。&/b&它指的是交易者能否克服实盘中的心理压力从而坚持使用这个策略。回测中的净值曲线通常是非常稳健的(几年的回测期内慢慢爬升),其各种指标 —— 收益率、最大回撤、夏普率等 —— 也非常出色。面对回测中优秀的净值表现,我们会错误的高估自己对于回测中亏损以及回撤周期的容忍程度。在实盘中面对如此的亏损和回撤周期完全是另一个 story。&/p&&p&&b&对于任何一个策略,几乎可以确定的是它在回测中的表现是其在实盘中表现的上限(比如以最大回撤而言,我们自己的经验和很多量化投资前辈的经验都指出,实盘的最大回撤差不多是回测中最大回撤的 2 倍)。&/b&此外,当一个策略上线之初,亏损先于利润出现也是十分常见的。面对真金白银的亏损,我们会比想象的更脆弱、更容易怀疑策略的开发中是否存在没有考虑到的问题、更容易自我动摇从而想要放弃该策略。在实际交易中,价格时刻在波动,充斥着噪音的各路消息以远超过我们能够接受的速度袭来,我们会快步踏入行为金融学中的各种认知偏差陷阱、丧失冷静、做出一系列干预交易系统的错误操作。这些心理压力是哪怕再精细、再严格的回测也无法呈现的。&/p&&p&另外,任何策略都会有失效的那一天。一个技术指标会因为使用的人越来越多而失效,一个选股因子会因为市场状态的变化而失效,一个赚钱的策略会因为流动性突然枯竭(比如股指期货的突然限制)而失效。&b&任何量化策略都需要定期进行事后评价,比较它在样本外和回测期内的表现,评判它是否仍然有效,或者根据实盘的反馈进行调整。&/b&&/p&&p&但是,评价策略这件事儿说来容易,做起来难。特别是对于低频策略,由于交易频率低,它们需要更长的实盘时间来评判策略是否失效。比如小市值选股因子失效是因为价值投资在最近两年盛行。然而,如果我们在这两年一直按照小市值选股却早已亏的一塌糊涂了。又比如,在资产配置策略中,随着实盘的进行,哪些投资品应该被挪出资产池、哪些回测中没有考虑的投资品又应该被加进来,这些都是回测无法回答的。&/p&&p&当然,这些并非回测之过。&b&一个经过科学回测的量化投资策略仍然是投资中的利器,它刻画了市场的某种具备清晰业务逻辑支撑的特性,并假设该特性在未来会重复,从而捕捉这些机会。&/b&如果市场变了,该特性不再发生而导致策略失效,这不是回测的问题,我们只要欣然接受它就是了。科学的回测让我们更有希望接近实现交易的一致性;坚持一致性的交易才能让我们成为更优秀的交易者;只有不断追求卓越、成为更优秀的交易者,才可能在市场的沉浮中生存的更久。&/p&&p&&br&&/p&&p&&br&&/p&&p&原创不易,请保护版权。如需转载,请联系获得授权,并注明出处,谢谢。已委托“维权骑士”(&a href=&https://link.zhihu.com/?target=http%3A//rightknights.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&维权骑士_免费版权监测/版权保护/版权分发&/a&) 为进行维权行动。&/p&
1 引言回测(backtesting)是任何一个量化投资策略上线之前必须要经过的一道流程;它是量化投资策略和其他类型投资(如主动投资)最大的区别之一。回测将投资策略应用到历史数据中并考察它的表现。具体的,回测有如下作用:验证投资策略是否有效:我们很容…
&figure&&img src=&https://pic1.zhimg.com/v2-ce2bec5208f8_b.jpg& data-rawwidth=&500& data-rawheight=&334& class=&origin_image zh-lightbox-thumb& width=&500& data-original=&https://pic1.zhimg.com/v2-ce2bec5208f8_r.jpg&&&/figure&&figure&&img src=&https://pic2.zhimg.com/v2-a3c6c8595ade65ff12d87_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1226& data-rawheight=&916& class=&origin_image zh-lightbox-thumb& width=&1226& data-original=&https://pic2.zhimg.com/v2-a3c6c8595ade65ff12d87_r.jpg&&&/figure&&p&这张图在知乎上我看到过,但是错过没保存,当时对这图的作者的概括能力表示无比敬仰。&/p&&p&今天没事逛&a href=&https://link.zhihu.com/?target=http%3A//www.pythontab.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&PythonTab:Python中文开发者社区门户&/a&,又看到了这张图,于是果断保存发布出来,侵删。&/p&&p&对于&b&&u&看过几遍入门教程&/u&&/b&的同学来说,下一步就是进入长时间的实战阶段,这个阶段会对之前似是而非的理解进行修正,会经常出错,bug满天飞。这个时候有这张神图作为辅助,那简直是地图插满眼啊。&/p&&p&&br&&/p&&p&希望能帮助到你。&/p&
这张图在知乎上我看到过,但是错过没保存,当时对这图的作者的概括能力表示无比敬仰。今天没事逛,又看到了这张图,于是果断保存发布出来,侵删。对于看过几遍入门教程的同学来说,下一步就是进入长时间的实战阶段,这…
&p&这个系列视频讲得比较全了,都是动画演示,非常直观。自己按需观看吧:&br&视频地址:&a href=&//link.zhihu.com/?target=http%3A//www.bilibili.com/video/av6731067/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&【双语字幕】「线性代数的本质」合集&/a&&br&内容目录:&br&&/p&&blockquote&第零讲:序言&br&第一讲:向量究竟是什么&br&第二讲:线性组合、张成的空间与基&br&第三讲:矩阵与线性变换&br&第四讲:矩阵乘法与线性变换的复合&br&第四讲附注:三维空间中的线性变换&br&第五讲:行列式的意义&br&第六讲:逆矩阵、列空间与零空间&br&第六讲附注:非方阵&br&第七讲:点积与对偶性&br&第八讲上:叉积的标准介绍&br&第八讲下:以线性变换的眼光看叉积&br&第九讲:基变换&br&第十讲:特征向量与特征值&br&第十一讲:抽象向量空间&/blockquote&视频原作者:&a href=&//link.zhihu.com/?target=http%3A//www.3blue1brown.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&3Blue1Brown&/a&(可汗学院的一位教师),字幕中译:&a href=&//link.zhihu.com/?target=http%3A//space.bilibili.com/3557916/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Solara570@Bilibili&/a&&br&&figure&&img src=&https://pic3.zhimg.com/50/v2-f0beda66a5eef93cc47eaa3_b.jpg& data-rawwidth=&4096& data-rawheight=&2304& class=&origin_image zh-lightbox-thumb& width=&4096& data-original=&https://pic3.zhimg.com/50/v2-f0beda66a5eef93cc47eaa3_r.jpg&&&/figure&&br&原作者3Blue1Brown最近在Patreon上发起众筹,说如果获得足够多的资助,他就可以辞去工作,全职做这类视频,以每月两部的速度发布。计划要做的视频系列包括「微积分的本质」「概率的本质」「实分析的本质」「复分析的本质」「常微分方程的本质」等等,此外还会经常制作像「&a href=&//link.zhihu.com/?target=http%3A//www.bilibili.com/video/av6385842/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&最速降线问题&/a&」「&a href=&//link.zhihu.com/?target=http%3A//www.bilibili.com/video/av4201747/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&希尔伯特曲线&/a&」那样的单个视频。目前他正开始着手制作「微积分的本质」系列,凡是在Patreon上资助了他的人可以抢先看,不必等到整个系列制作完毕。&br&&br&资助网址是:&a href=&//link.zhihu.com/?target=https%3A//www.patreon.com/3blue1brown& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://www.&/span&&span class=&visible&&patreon.com/3blue1brown&/span&&span class=&invisible&&&/span&&/a& ,诸位若有条件不妨去支持一下。最低额度只要1美元/个视频,就能优先观看今后制作的所有「XX的本质」系列视频。(刚发现,Minecraft的作者Markus Persson赫然出现在资助者名单中……)&br&&br&另外还有个好消息:应B站几位字幕译者的邀请,3Blue1Brown本人已同意在B站设立官方账号:&a href=&//link.zhihu.com/?target=http%3A//space.bilibili.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&3Blue1Brown @ Bilibili&/a&,今后他在Youtube发布的精彩视频都会在B站上持续发布中文字幕版,大家可以去B站关注和支持。
这个系列视频讲得比较全了,都是动画演示,非常直观。自己按需观看吧: 视频地址: 内容目录: 第零讲:序言 第一讲:向量究竟是什么 第二讲:线性组合、张成的空间与基 第三讲:矩阵与线性变换 第四讲:矩阵乘法与线性…
&figure&&img src=&https://pic4.zhimg.com/f2bb92cfbdfdfe136fd8d41e_b.jpg& data-rawwidth=&720& data-rawheight=&450& class=&origin_image zh-lightbox-thumb& width=&720& data-original=&https://pic4.zhimg.com/f2bb92cfbdfdfe136fd8d41e_r.jpg&&&/figure&&p&&b&开篇申明:笔者也是python初学者,以下内容纯粹从个人使用经验来讨论问题,若有谬误,请大神们拍砖。&/b&&/p&Python where we can, C++ where we must,这句话基本概率了python大法对CTA的重要作用。不是说迷信python大法好,而是说python特别符合CTA策略研发的一些特征,即利用了python的一些优势,又很好的回避了它的一些局限性。原因有如下3个:&p&1. 代码简洁,开发效率高。CTA策略研发需要大量来回调参,修改局部代码,属于典型的投入多,产出少。尝试几十几百个策略,成果却可能就那么1-2个,这种事情更是家常便饭。python代码极为简洁,语法简单,能让开发人员从大量的重复体力劳动中解脱出来,乃懒人同胞居家必备之良药,实在是药不能停;&/p&&p&2. 学习曲线友好,适合编程入门。CTA策略主要是使用python大法里数据处理这一小部分内容,主要包括pandas, numpy, scipy, statsmodels, sklearn, matplotlib, os, cmath, logging这些模块,其他的通通不学基本没有任何关系;&/p&&p&3. 至于python速度和性能上的局限性,我个人觉得没啥问题。1是因为反正交易下单系统还是C++搞定,不需要python。2是我又没处理tick,数据量又不是很恐怖,numpy基本能轻松搞定一切;&/p&&br&&p&————————————————干货分割线————————————————&/p&&p&废话不多说,亮兵器。&/p&&p&&b&python的学习,同其他语种相比,是一件无比快乐的事情。
&/b&——By 笔者&/p&&p&1. python入门书籍,推荐head first,基本算是零基础入门读物,内容主要包括常用函数,OI读写和简单的数据处理。我网上找到了中文版的PDF,&b&若需要强烈建议购买正版书籍&/b&。下载链接:&/p&&p&&a href=&http://link.zhihu.com/?target=http%3A//download.csdn.net/detail/dqsweet/5751259& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Head_First_Python(中文版).pdf&/a&&br&&/p&&p&2. python进阶书籍,推荐&i&Python For Data Analysis&/i&或者&i&Python数据分析基础教程:NumPy学习指南&/i&,主要是数据处理的一些常用工具的使用,包括numpy, pandas和matplotlib。其中&i&Python For Data Analysis&/i&网上暂未找到中文版的PDF,&b&若需要强烈建议购买正版书籍&/b&。下载链接:&/p&&p&&a href=&http://link.zhihu.com/?target=http%3A//download.csdn.net/download/u72537& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Python For Data Analysis (2013).pdf&/a&&br&&/p&&p&&a href=&http://link.zhihu.com/?target=http%3A//download.csdn.net/detail/cacasy/7150141& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Python数据分析基础教程&/a&&br&&/p&&p&3. 细节参考书籍。请自己百度&i&python基础教程&/i&或其他同类型资料&i&;&/i&&/p&&p&4. 至于statsmodels, sklearn这2个模块的话,由于开发时间不是很久,而且现阶段还算是未收官的模块包,网上暂时找不到相关的书籍。大家可以看看对应的开发手册或者网上一些帖子的简单介绍;&/p&&p&另外说一下几点个人的使用经验:&/p&&p&1. 从效率上来说,能用numpy搞定的不要用pandas。或者换句话说,能用iloc的地方尽量用iloc,除非涉及到时间序列上数据的对齐和文本信息处理这2个方面,再考虑pandas;&/p&&p&2. CTA策略研发总体plot用的还是不多,个人建议是尽量不要或者少在美化和界面上投入时间,投入时间多又没产出,不过是讨老板开心而已,何况你有很多更重要的事情要做;&/p&&p&3. 什么时候用nan, inf和0是个很大的学问,慎之慎之又慎之;&/p&&p&4. 多用logging模块,尽量别用或少用print,长痛不如短痛;&/p&&p&5. paramiko大法好;&/p&&br&&p&&b&最后,祝大家自学开心:)&/b&&/p&
开篇申明:笔者也是python初学者,以下内容纯粹从个人使用经验来讨论问题,若有谬误,请大神们拍砖。Python where we can, C++ where we must,这句话基本概率了python大法对CTA的重要作用。不是说迷信python大法好,而是说python特别符合CTA策略研发的一些…
&figure&&img src=&https://pic2.zhimg.com/v2-53a59fcf0e4fbd3deeced82e986c4c03_b.jpg& data-rawwidth=&347& data-rawheight=&220& class=&content_image& width=&347&&&/figure&&p&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-a6e41d7469aaebe5669ece_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&347& data-rawheight=&220& class=&content_image& width=&347&&&/figure&&p&&br&标准安装的Python中用列表(list)保存一组值,可以用来当作数组使用,不过由于列表的元素可以是任何对象,因此列表中所保存的是对象的指 针。这样为了保存一个简单的[1,2,3],需要有3个指针和三个整数对象。对于数值运算来说这种结构显然比较浪费内存和CPU计算时间。&br&此外Python还提供了一个array模块,array对象和列表不同,它直接保存数值,和C语言的一维数组比较类似。但是由于它不支持多维,也没有各种运算函数,因此也不适合做数值运算。&br&NumPy的诞生弥补了这些不足,NumPy提供了两种基本的对象:ndarray(N-dimensional array object)和 ufunc(universal function object)。ndarray(下文统一称之为数组)是存储单一数据类型的多维数组,而ufunc则是能够对数组进行处理的函数。&b&喜欢的话关注收藏评论转发比心么么哒!Python学习交流企-鹅-群-719-139-688内有大量的项目开发和新手教学视频PDF书籍的千人大群等着你来加入&/b&&br&&b&2.1 ndarray对象&/b&&br&函数库的导入&br&本书的示例程序假设用以下推荐的方式导入NumPy函数库:&br&import numpy as np&br&&b&2.1.1 创建&/b&&br&首先需要创建数组才能对其进行其它操作。&br&我们可以通过给array函数传递Python的序列对象创建数组,如果传递的是多层嵌套的序列,将创建多维数组(下例中的变量c):&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-e2281985bed6e4b27a42e_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&419& data-rawheight=&391& class=&content_image& width=&419&&&/figure&&p&&br&数组a的shape只有一个元素,因此它是一维数组。而数组c的shape有两个元素,因此它是二维数组,其中第0轴的长度为3,第1轴的长度为 4。还可以通过修改数组的shape属性,在保持数组元素个数不变的情况下,改变数组每个轴的长度。下面的例子将数组c的shape改为(4,3),注意 从(3,4)改为(4,3)并不是对数组进行转置,而只是改变每个轴的大小,数组元素在内存中的位置并没有改变:&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-8a0b3ad587b6efebb304c3_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&192& data-rawheight=&140& class=&content_image& width=&192&&&/figure&&p&&br&当某个轴的元素为-1时,将根据数组元素的个数自动计算此轴的长度,因此下面的程序将数组c的shape改为了(2,6):&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-02bf8baad26fae32d9e5c14c_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&268& data-rawheight=&103& class=&content_image& width=&268&&&/figure&&p&&br&使用数组的reshape方法,可以创建一个改变了尺寸的新数组,原数组的shape保持不变:&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-f750e1d85e1a77aff2ef_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&263& data-rawheight=&144& class=&content_image& width=&263&&&/figure&&p&&br&数组a和d其实共享数据存储内存区域,因此修改其中任意一个数组的元素都会同时修改另外一个数组的内容:&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-258f3ca37a4ef440692de_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&309& data-rawheight=&106& class=&content_image& width=&309&&&/figure&&p&&br&数组的元素类型可以通过dtype属性获得。上面例子中的参数序列的元素都是整数,因此所创建的数组的元素类型也是整数,并且是32bit的长整型。可以通过dtype参数在创建时指定元素类型:&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-b0daafe38c144eb57710db_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&504& data-rawheight=&191& class=&origin_image zh-lightbox-thumb& width=&504& data-original=&https://pic1.zhimg.com/v2-b0daafe38c144eb57710db_r.jpg&&&/figure&&p&&br&上面的例子都是先创建一个Python序列,然后通过array函数将其转换为数组,这样做显然效率不高。因此NumPy提供了很多专门用来创建数组的函数。下面的每个函数都有一些关键字参数,具体用法请查看函数说明。&br&&/p&&ul&&li&arange函数类似于python的range函数,通过指定开始值、终值和步长来创建一维数组,注意数组不包括终值:&/li&&/ul&&figure&&img src=&https://pic3.zhimg.com/v2-f697ebfedda653b8411126_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&451& data-rawheight=&50& class=&origin_image zh-lightbox-thumb& width=&451& data-original=&https://pic3.zhimg.com/v2-f697ebfedda653b8411126_r.jpg&&&/figure&&p&&br&linspace函数通过指定开始值、终值和元素个数来创建一维数组,可以通过endpoint关键字指定是否包括终值,缺省设置是包括终值:&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-22dcd4d8e9c3b_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&455& data-rawheight=&91& class=&origin_image zh-lightbox-thumb& width=&455& data-original=&https://pic3.zhimg.com/v2-22dcd4d8e9c3b_r.jpg&&&/figure&&p&&br&logspace函数和linspace类似,不过它创建等比数列,下面的例子产生1(10^0)到100(10^2)、有20个元素的等比数列:&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-3ba067bfd86d290fac16fe_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&427& data-rawheight=&142& class=&origin_image zh-lightbox-thumb& width=&427& data-original=&https://pic4.zhimg.com/v2-3ba067bfd86d290fac16fe_r.jpg&&&/figure&&p&&br&此外,使用frombuffer, fromstring, fromfile等函数可以从字节序列创建数组,下面以fromstring为例:&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-6dabc029b1ff3f72bb57_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&161& data-rawheight=&45& class=&content_image& width=&161&&&/figure&&p&&br&Python的字符串实际上是字节序列,每个字符占一个字节,因此如果从字符串s创建一个8bit的整数数组的话,所得到的数组正好就是字符串中每个字符的ASCII编码:&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-aa6fd732e4e_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&389& data-rawheight=&56& class=&content_image& width=&389&&&/figure&&p&&br&如果从字符串s创建16bit的整数数组,那么两个相邻的字节就表示一个整数,把字节98和字节97当作一个16位的整数,它的值就是98*256+97 = 25185。可以看出内存中是以little endian(低位字节在前)方式保存数据的。&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-d0babb974b814090cfa31f1_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&409& data-rawheight=&94& class=&content_image& width=&409&&&/figure&&p&&br&如果把整个字符串转换为一个64位的双精度浮点数数组,那么它的值是:&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-7aae59ef89be18c6648ea_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&251& data-rawheight=&43& class=&content_image& width=&251&&&/figure&&p&&br&显然这个例子没有什么意义,但是可以想象如果我们用C语言的二进制方式写了一组double类型的数值到某个文件中,那们可以从此文件读取相应的数据,并通过fromstring函数将其转换为float64类型的数组。&br&我们可以写一个Python的函数,它将数组下标转换为数组中对应的值,然后使用此函数创建数组:&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-cbace5e36e426fd2565eec_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&397& data-rawheight=&131& class=&content_image& width=&397&&&/figure&&p&&br&fromfunction函数的第一个参数为计算每个数组元素的函数,第二个参数为数组的大小(shape),因为它支持多维数组,所以第二个参数必须是一个序列,本例中用(10,)创建一个10元素的一维数组。&br&下面的例子创建一个二维数组表示九九乘法表,输出的数组a中的每个元素a[i, j]都等于func2(i, j):&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-f01b78ddedee_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&404& data-rawheight=&319& class=&content_image& width=&404&&&/figure&&p&&br&&b&2.1.2 存取元素&/b&&br&数组元素的存取方法和Python的标准方法相同:&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-5f63ebaafcf2c398efbd9e4e8aff86ed_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&454& data-rawheight=&418& class=&origin_image zh-lightbox-thumb& width=&454& data-original=&https://pic2.zhimg.com/v2-5f63ebaafcf2c398efbd9e4e8aff86ed_r.jpg&&&/figure&&p&&br&和Python的列表序列不同,通过下标范围获取的新的数组是原始数组的一个视图。它与原始数组共享同一块数据空间:&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-b5ba61a5aa439c6fad6a_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&451& data-rawheight=&184& class=&origin_image zh-lightbox-thumb& width=&451& data-original=&https://pic2.zhimg.com/v2-b5ba61a5aa439c6fad6a_r.jpg&&&/figure&&p&&br&除了使用下标范围存取元素之外,NumPy还提供了两种存取元素的高级方法。&br&&b&使用整数序列&/b&&br&当使用整数序列对数组元素进行存取时,将使用整数序列中的每个元素作为下标,整数序列可以是列表或者数组。使用整数序列作为下标获得的数组不和原始数组共享数据空间。&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-98aab2c817ab00b0b280c59c0fe678c6_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&472& data-rawheight=&318& class=&origin_image zh-lightbox-thumb& width=&472& data-original=&https://pic4.zhimg.com/v2-98aab2c817ab00b0b280c59c0fe678c6_r.jpg&&&/figure&&p&&br&&b&使用布尔数组&/b&&br&当使用布尔数组b作为下标存取数组x中的元素时,将收集数组x中所有在数组b中对应下标为True的元素。使用布尔数组作为下标获得的数组不和原始数组共享数据空间,注意这种方式只对应于布尔数组,不能使用布尔列表。&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-458cc0ba146fcb52aba7_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&497& data-rawheight=&375& class=&origin_image zh-lightbox-thumb& width=&497& data-original=&https://pic1.zhimg.com/v2-458cc0ba146fcb52aba7_r.jpg&&&/figure&&p&&br&布尔数组一般不是手工产生,而是使用布尔运算的ufunc函数产生,关于ufunc函数请参照 ufunc运算 一节。&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-fcec5baf08c8ba555f9ff248e4e97917_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&564& data-rawheight=&249& class=&origin_image zh-lightbox-thumb& width=&564& data-original=&https://pic1.zhimg.com/v2-fcec5baf08c8ba555f9ff248e4e97917_r.jpg&&&/figure&&p&&br&&b&2.1.3 多维数组&/b&&br&多维数组的存取和一维数组类似,因为多维数组有多个轴,因此它的下标需要用多个值来表示,NumPy采用组元(tuple)作为数组的下标。如图2.1所示,a为一个6x6的数组,图中用颜色区分了各个下标以及其对应的选择区域。&br&组元不需要圆括号&br&虽然我们经常在Python中用圆括号将组元括起来,但是其实组元的语法定义只需要用逗号隔开即可,例如 x,y=y,x 就是用组元交换变量值的一个例子。&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-2d74aacd0b4e0b93f93c_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&557& data-rawheight=&314& class=&origin_image zh-lightbox-thumb& width=&557& data-original=&https://pic3.zhimg.com/v2-2d74aacd0b4e0b93f93c_r.jpg&&&/figure&&p&&br&&b&如何创建这个数组&/b&&br&你也许会对如何创建a这样的数组感到好奇,数组a实际上是一个加法表,纵轴的值为0, 10, 20, 30, 40, 50;横轴的值为0, 1, 2, 3, 4, 5。纵轴的每个元素都和横轴的每个元素求和,就得到图中所示的数组a。你可以用下面的语句创建它,至于其原理我们将在后面的章节进行讨论:&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-dd7ab814d66ec7d8eb76064e_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&371& data-rawheight=&171& class=&content_image& width=&371&&&/figure&&p&&br&多维数组同样也可以使用整数序列和布尔数组进行存取。&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-0d36a12f16d5eb5431eea_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&534& data-rawheight=&260& class=&origin_image zh-lightbox-thumb& width=&534& data-original=&https://pic3.zhimg.com/v2-0d36a12f16d5eb5431eea_r.jpg&&&/figure&&p&&br&&/p&&ul&&li&a[(0,1,2,3,4),(1,2,3,4,5)] : 用于存取数组的下标和仍然是一个有两个元素的组元,组元中的每个元素都是整数序列,分别对应数组的第0轴和第1轴。从两个序列的对应位置取出两个整数组成 下标: a[0,1], a[1,2], ..., a[4,5]。&/li&&li&a[3:, [0, 2, 5]] : 下标中的第0轴是一个范围,它选取第3行之后的所有行;第1轴是整数序列,它选取第0, 2, 5三列。&/li&&li&a[mask, 2] : 下标的第0轴是一个布尔数组,它选取第0,2,5行;第1轴是一个整数,选取第2列。&/li&&/ul&&p&&b&2.1.4 结构数组&/b&&br&在C语言中我们可以通过struct关键字定义结构类型,结构中的字段占据连续的内存空间,每个结构体占用的内存大小都相同,因此可以很容易地定义 结构数组。和C语言一样,在NumPy中也很容易对这种结构数组进行操作。只要NumPy中的结构定义和C语言中的定义相同,NumPy就可以很方便地读 取C语言的结构数组的二进制数据,转换为NumPy的结构数组。&br&假设我们需要定义一个结构数组,它的每个元素都有name, age和weight字段。在NumPy中可以如下定义:&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-f0e6bffe85ca_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&356& data-rawheight=&146& class=&content_image& width=&356&&&/figure&&p&&br&我们先创建一个dtype对象persontype,通过其字典参数描述结构类型的各个字段。字典有两个关键字:names,formats。每个关键字对应的值都是一个列表。names定义结构中的每个字段名,而formats则定义每个字段的类型:&br&&/p&&ul&&li&&b&S32&/b& : 32个字节的字符串类型,由于结构中的每个元素的大小必须固定,因此需要指定字符串的长度&/li&&li&&b&i&/b& : 32bit的整数类型,相当于np.int32&/li&&li&&b&f&/b& : 32bit的单精度浮点数类型,相当于np.float32&/li&&/ul&&p&然后我们调用array函数创建数组,通过关键字参数 dtype=persontype, 指定所创建的数组的元素类型为结构persontype。运行上面程序之后,我们可以在IPython中执行如下的语句查看数组a的元素类型&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-d4f797f6d5bf120dbc7aaab298e71eb7_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&407& data-rawheight=&51& class=&content_image& width=&407&&&/figure&&p&&br&这里我们看到了另外一种描述结构类型的方法: 一个包含多个组元的列表,其中形如 (字段名, 类型描述) 的组元描述了结构中的每个字段。类型描述前面为我们添加了 '|', '&' 等字符,这些字符用来描述字段值的字节顺序:&br&&/p&&ul&&li&&b&|&/b& : 忽视字节顺序&/li&&li&&b&&&/b& : 低位字节在前&/li&&li&&b&&&/b& : 高位字节在前&/li&&/ul&&p&结构数组的存取方式和一般数组相同,通过下标能够取得其中的元素,注意元素的值看上去像是组元,实际上它是一个结构:&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-b57c6a510beca9afba970_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&523& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic2.zhimg.com/v2-b57c6a510beca9afba970_r.jpg&&&/figure&&p&&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-db5fa12e86d5_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&632& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic2.zhimg.com/v2-db5fa12e86d5_r.jpg&&&/figure&&p&&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-f4afe79a84b61b353f2ffd8d03f99ada_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&285& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic1.zhimg.com/v2-f4afe79a84b61b353f2ffd8d03f99ada_r.jpg&&&/figure&&p&&br&&b&2.1.5 内存结构&/b&&br&下面让我们来看看ndarray数组对象是如何在内存中储存的。如图2.3所示,关于数组的描述信息保存在一个数据结构中,这个结构引用两个对象:一块用于保存数据的存储区域和一个用于描述元素类型的dtype对象。&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-64e1a0ac2eef7ce88a9fc2cc5363fded_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&500& data-rawheight=&280& class=&origin_image zh-lightbox-thumb& width=&500& data-original=&https://pic4.zhimg.com/v2-64e1a0ac2eef7ce88a9fc2cc5363fded_r.jpg&&&/figure&&p&&br&图2.3 ndarray数组对象在内存中的储存方式&br&数据存储区域保存着数组中所有元素的二进制数据,dtype对象则知道如何将元素的二进制数据转换为可用的值。数组的维数、大小等信息都保存在ndarray数组对象的数据结构中。图中显示的是如下数组的内存结构:&br&&&& a = np.array([[0,1,2],[3,4,5],[6,7,8]], dtype=np.float32)strides中保存的是当每个轴的下标增加1时,数据存储区中的指针所增加的字节数。例如图中的strides为12,4,即第0轴的下标增加1 时,数据的地址增加12个字节:即a[1,0]的地址比a[0,0]的地址要高12个字节,正好是3个单精度浮点数的总字节数;第1轴下标增加1时,数据 的地址增加4个字节,正好是单精度浮点数的字节数。&br&如果strides中的数值正好和对应轴所占据的字节数相同的话,那么数据在内存中是连续存储的。然而数据并不一定都是连续储存的,前面介绍过通过下标范围得到新的数组是原始数组的视图,即它和原始视图共享数据存储区域:&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-b778d626e6fe9e2ac079_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&215& data-rawheight=&141& class=&content_image& width=&215&&&/figure&&p&&br&由于数组b和数组a共享数据存储区,而b中的第0轴和第1轴都是数组a中隔一个元素取一个,因此数组b的strides变成了24,8,正好都是数组a的两倍。 对照前面的图很容易看出数据0和2的地址相差8个字节,而0和6的地址相差24个字节。&br&元素在数据存储区中的排列格式有两种:C语言格式和Fortan语言格式。在C语言中,多维数组的第0轴是最上位的,即第0轴的下标增加1时,元素 的地址增加的字节数最多;而Fortan语言的多维数组的第0轴是最下位的,即第0轴的下标增加1时,地址只增加一个元素的字节数。在NumPy中,元素 在内存中的排列缺省是以C语言格式存储的,如果你希望改为Fortan格式的话,只需要给数组传递order=&F&参数:&br&&&& c = np.array([[0,1,2],[3,4,5],[6,7,8]], dtype=np.float32, order=&F&)&&& c.strides(4, 12)&b&2.2 ufunc运算&/b&&br&ufunc是universal function的缩写,它是一种能对数组的每个元素进行操作的函数。NumPy内置的许多ufunc函数都是在C语言级别实现的,因此它们的计算速度非常快。让我们来看一个例子:&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-84d70dff2a48e122922d_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&406& data-rawheight=&193& class=&content_image& width=&406&&&/figure&&p&&br&先用linspace产生一个从0到2*PI的等距离的10个数,然后将其传递给sin函数,由于np.sin是一个ufunc函数,因此它对x中 的每个元素求正弦值,然后将结果返回,并且赋值给y。计算之后x中的值并没有改变,而是新创建了一个数组保存结果。如果我们希望将sin函数所计算的结果 直接覆盖到数组x上去的话,可以将要被覆盖的数组作为第二个参数传递给ufunc函数。例如::&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-42fcb03ff04bf2e6390200_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&386& data-rawheight=&189& class=&content_image& width=&386&&&/figure&&p&&br&sin函数的第二个参数也是x,那么它所做的事情就是对x中的每给值求正弦值,并且把结果保存到x中的对应的位置中。此时函数的返回值仍然是整个计算的结果,只不过它就是x,因此两个变量的id是相同的(变量t和变量x指向同一块内存区域)。&br&我用下面这个小程序,比较了一下numpy.math和Python标准库的math.sin的计算速度::&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-a728c567d68f7dd02e84f57ecf76bea7_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&298& data-rawheight=&436& class=&content_image& width=&298&&&/figure&&p&&br&在我的电脑上计算100万次正弦值,numpy.sin比math.sin快10倍多。这得利于numpy.sin在C语言级别的循环计算。 numpy.sin同样也支持对单个数值求正弦,例如:numpy.sin(0.5)。不过值得注意的是,对单个数的计算math.sin则比 numpy.sin快得多了,让我们看下面这个测试程序:&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-cc1f183b1a966d8c0ea893_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&300& data-rawheight=&186& class=&content_image& width=&300&&&/figure&&p&&br&请注意numpy.sin的计算速度只有math.sin的1/5。这是因为numpy.sin为了同时支持数组和单个值的计算,其C语言的内部实 现要比math.sin复杂很多,如果我们同样在Python级别进行循环的话,就会看出其中的差别了。此外,numpy.sin返回的数的类型和 math.sin返回的类型有所不同,math.sin返回的是Python的标准float类型,而numpy.sin则返回一个 numpy.float64类型:&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-a9d0cde562aa6c401707_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&182& data-rawheight=&104& class=&content_image& width=&182&&&/figure&&p&&br&通过上面的例子我们了解了如何最有效率地使用math库和numpy库中的数学函数。因为它们各有长短,因此在导入时不建议使用*号全部载入,而是应该使用import numpy as np的方式载入,这样我们可以根据需要选择合适的函数调用。&br&NumPy中有众多的ufunc函数为我们提供各式各样的计算。除了sin这种单输入函数之外,还有许多多个输入的函数,add函数就是一个最常用的例子。先来看一个例子:&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-8d18d92cc22f983beaf2240e4ece92ac_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&191& data-rawheight=&277& class=&content_image& width=&191&&&/figure&&p&&br&add函数返回一个新的数组,此数组的每个元素都为两个参数数组的对应元素之和。它接受第3个参数指定计算结果所要写入的数组,如果指定的话,add函数就不再产生新的数组。&br&由于Python的操作符重载功能,计算两个数组相加可以简单地写为a+b,而np.add(a,b,a)则可以用a+=b来表示。下面是数组的运 算符和其对应的ufunc函数的一个列表,注意除号&/&的意义根据是否激活__future__.division有所不同。&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-c12147adb479d_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&526& data-rawheight=&253& class=&origin_image zh-lightbox-thumb& width=&526& data-original=&https://pic4.zhimg.com/v2-c12147adb479d_r.jpg&&&/figure&&p&&br&数组对象支持这些操作符,极大地简化了算式的编写,不过要注意如果你的算式很复杂,并且要运算的数组很大的话,会因为产生大量的中间结果而降低程序的运算效率。例如:假设a b c三个数组采用算式x=a*b+c计算,那么它相当于:&br&t = a * bx = t + cdel t也就是说需要产生一个数组t保存乘法的计算结果,然后再产生最后的结果数组x。我们可以通过手工将一个算式分解为x = a*b; x += c,以减少一次内存分配。&br&通过组合标准的ufunc函数的调用,可以实现各种算式的数组计算。不过有些时候这种算式不易编写,而针对每个元素的计算函数却很容易用 Python实现,这时可以用frompyfunc函数将一个计算单个元素的函数转换成ufunc函数。这样就可以方便地用所产生的ufunc函数对数组 进行计算了。让我们来看一个例子。&br&我们想用一个分段函数描述三角波,三角波的样子如图2.4所示:&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-ffdfb698f79a_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&278& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic2.zhimg.com/v2-ffdfb698f79a_r.jpg&&&/figure&&p&&br&图2.4 三角波可以用分段函数进行计算&br&我们很容易根据上图所示写出如下的计算三角波某点y坐标的函数:&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-01e783bc2842cfba5b00_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&442& data-rawheight=&138& class=&origin_image zh-lightbox-thumb& width=&442& data-original=&https://pic4.zhimg.com/v2-01e783bc2842cfba5b00_r.jpg&&&/figure&&p&&br&显然triangle_wave函数只能计算单个数值,不能对数组直接进行处理。我们可以用下面的方法先使用列表包容(List comprehension),计算出一个list,然后用array函数将列表转换为数组:&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-5fbdc6e053f089bd4050c90_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&385& data-rawheight=&54& class=&content_image& width=&385&&&/figure&&p&&br&这种做法每次都需要使用列表包容语法调用函数,对于多维数组是很麻烦的。让我们来看看如何用frompyfunc函数来解决这个问题:&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-c6a3a188e483b0bbb9e10adfe07f81ec_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&510& data-rawheight=&51& class=&origin_image zh-lightbox-thumb& width=&510& data-original=&https://pic1.zhimg.com/v2-c6a3a188e483b0bbb9e10adfe07f81ec_r.jpg&&&/figure&&p&&br&frompyfunc的调用格式为frompyfunc(func, nin, nout),其中func是计算单个元素的函数,nin是此函数的输入参数的个数,nout是此函数的返回值的个数。虽然triangle_wave函数 有4个参数,但是由于后三个c, c0, hc在整个计算中值都是固定的,因此所产生的ufunc函数其实只有一个参数。为了满足这个条件,我们用一个lambda函数对 triangle_wave的参数进行一次包装。这样传入frompyfunc的函数就只有一个参数了。这样子做,效率并不是太高,另外还有一种方法:&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-99a075fbf089bb6_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&481& data-rawheight=&294& class=&origin_image zh-lightbox-thumb& width=&481& data-original=&https://pic4.zhimg.com/v2-99a075fbf089bb6_r.jpg&&&/figure&&p&&br&我们通过函数triangle_func包装三角波的三个参数,在其内部定义一个计算三角波的函数trifunc,trifunc函数在调用时会采用triangle_func的参数进行计算。最后triangle_func返回用frompyfunc转换结果。&br&值得注意的是用frompyfunc得到的函数计算出的数组元素的类型为object,因为frompyfunc函数无法保证Python函数返回的数据类型都完全一致。因此还需要再次 y2.astype(np.float64)将其转换为双精度浮点数组。&br&&b&2.2.1 广播&/b&&br&当我们使用ufunc函数对两个数组进行计算时,ufunc函数会对这两个数组的对应元素进行计算,因此它要求这两个数组有相同的大小(shape相同)。如果两个数组的shape不同的话,会进行如下的广播(broadcasting)处理:&br&&/p&&ol&&li&让所有输入数组都向其中shape最长的数组看齐,shape中不足的部分都通过在前面加1补齐&/li&&li&输出数组的shape是输入数组shape的各个轴上的最大值&/li&&li&如果输入数组的某个轴和输出数组的对应轴的长度相同或者其长度为1时,这个数组能够用来计算,否则出错&/li&&li&当输入数组的某个轴的长度为1时,沿着此轴运算时都用此轴上的第一组值&/li&&/ol&&p&上述4条规则理解起来可能比较费劲,让我们来看一个实际的例子。&br&先创建一个二维数组a,其shape为(6,1):&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-c2c92e037dcd1e4d625d_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&282& data-rawheight=&270& class=&content_image& width=&282&&&/figure&&p&&br&计算a和b的和,得到一个加法表,它相当于计算a,b中所有元素组的和,得到一个shape为(6,5)的数组:&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-650cc701fd48a2a1dd02e74a85dc264e_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&209& data-rawheight=&234& class=&content_image& width=&209&&&/figure&&p&&br&由于a和b的shape长度(也就是ndim属性)不同,根据规则1,需要让b的shape向a对齐,于是将b的shape前面加1,补齐为(1,5)。相当于做了如下计算:&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-d63f5de154cc2_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&166& data-rawheight=&71& class=&content_image& width=&166&&&/figure&&p&&br&这样加法运算的两个输入数组的shape分别为(6,1)和(1,5),根据规则2,输出数组的各个轴的长度为输入数组各个轴上的长度的最大值,可知输出数组的shape为(6,5)。&br&由于b的第0轴上的长度为1,而a的第0轴上的长度为6,因此为了让它们在第0轴上能够相加,需要将b在第0轴上的长度扩展为6,这相当于:&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-de426bb9ecf9152ef37aea_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&176& data-rawheight=&188& class=&content_image& width=&176&&&/figure&&p&&br&由于a的第1轴的长度为1,而b的第一轴长度为5,因此为了让它们在第1轴上能够相加,需要将a在第1轴上的长度扩展为5,这相当于:&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-31f8f1e7a803ea71fbc259bbfe93ba27_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&348& data-rawheight=&219& class=&content_image& width=&348&&&/figure&&p&&br&当然,numpy在执行a+b运算时,其内部并不会真正将长度为1的轴用repeat函数进行扩展,如果这样做的话就太浪费空间了。&br&由于这种广播计算很常用,因此numpy提供了一个快速产生如上面a,b数组的方法: ogrid对象:&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-768ab7fe2ac0_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&214& data-rawheight=&202& class=&content_image& width=&214&&&/figure&&p&&br&ogrid是一个很有趣的对象,它像一个多维数组一样,用切片组元作为下标进行存取,返回的是一组可以用来广播计算的数组。其切片下标有两种形式:&br&&/p&&ul&&li&开始值:结束值:步长,和np.arange(开始值, 结束值, 步长)类似&/li&&li&开始值:结束值:长度j,当第三个参数为虚数时,它表示返回的数组的长度,和np.linspace(开始值, 结束值, 长度)类似:&/li&&/ul&&figure&&img src=&https://pic4.zhimg.com/v2-d0be8e84ee_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&235& data-rawheight=&187& class=&content_image& width=&235&&&/figure&&p&&br&ogrid为什么不是函数&br&根据Python的语法,只有在中括号中才能使用用冒号隔开的切片语法,如果ogrid是函数的话,那么这些切片必须使用slice函数创建,这显然会增加代码的长度。&br&利用ogrid的返回值,我能很容易计算x, y网格面上各点的值,或者x, y, z网格体上各点的值。下面是绘制三维曲面 x * exp(x**2 - y**2) 的程序:&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-f6be61d608be4f_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&307& data-rawheight=&210& class=&content_image& width=&307&&&/figure&&p&&br&此程序使用mayavi的mlab库快速绘制如图2.5所示的3D曲面,关于mlab的相关内容将在今后的章节进行介绍。&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-cedd383a7addccb27299b6_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&404& data-rawheight=&322& class=&content_image& width=&404&&&/figure&&p&&br&图2.5 使用ogrid创建的三维曲面&br&&b&2.2.2 ufunc的方法&/b&&br&ufunc函数本身还有些方法,这些方法只对两个输入一个输出的ufunc函数有效,其它的ufunc对象调用这些方法时会抛出ValueError异常。&br&&b&reduce&/b& 方法和Python的reduce函数类似,它沿着axis轴对array进行操作,相当于将&op&运算符插入到沿axis轴的所有子数组或者元素当中。&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-d96e796ceebcaca53d3efa_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&394& data-rawheight=&171& class=&content_image& width=&394&&&/figure&&p&&br&&b&accumulate&/b& 方法和reduce方法类似,只是它返回的数组和输入的数组的shape相同,保存所有的中间计算结果:&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-bb_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&316& data-rawheight=&122& class=&content_image& width=&316&&&/figure&&p&&br&&b&reduceat&/b& 方法计算多组reduce的结果,通过indices参数指定一系列reduce的起始和终了位置。reduceat的计算有些特别,让我们通过一个例子来解释一下:&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-22cab14f4d90_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&341& data-rawheight=&98& class=&content_image& width=&341&&&/figure&&p&&br&对于indices中的每个元素都会调用reduce函数计算出一个值来,因此最终计算结果的长度和indices的长度相同。结果result数组中除最后一个元素之外,都按照如下计算得出:&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-ec31f71ae_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&321& data-rawheight=&365& class=&content_image& width=&321&&&/figure&&p&&br&可以看出result[::2]和a相等,而result[1::2]和np.add.accumulate(a)相等。&br&&b&outer&/b& 方法,&op&.outer(a,b)方法的计算等同于如下程序:&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-e8d124b3098bea9b0203_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&204& data-rawheight=&79& class=&content_image& width=&204&&&/figure&&p&&br&其中squeeze的功能是剔除数组a中长度为1的轴。如果你看不太明白这个等同程序的话,让我们来看一个例子:&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-a60fe0c2b19b963a1f4ae77_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&284& data-rawheight=&139& class=&content_image& width=&284&&&/figure&&p&&br&可以看出通过outer方法计算的结果是如下的乘法表:&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-c0a9acc66d81f615e93c22cb8769d6ac_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&578& data-rawheight=&170& class=&origin_image zh-lightbox-thumb& width=&578& data-original=&https://pic4.zhimg.com/v2-c0a9acc66d81f615e93c22cb8769d6ac_r.jpg&&&/figure&&p&&br&&b&2.3 矩阵运算&/b&&br&NumPy和Matlab不一样,对于多维数组的运算,缺省情况下并不使用矩阵运算,如果你希望对数组进行矩阵运算的话,可以调用相应的函数。&br&matrix对象&br&numpy库提供了matrix类,使用matrix类创建的是矩阵对象,它们的加减乘除运算缺省采用矩阵方式计算,因此用法和matlab十分类 似。但是由于NumPy中同时存在ndarray和matrix对象,因此用户很容易将两者弄混。这有违Python的“显式优于隐式”的原则,因此并不 推荐在较复杂的程序中使用matrix。下面是使用matrix的一个例子:&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-7c2cb51a2cb61b06ad81c63b_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&409& data-rawheight=&129& class=&content_image& width=&409&&&/figure&&p&&br&因为a是用matrix创建的矩阵对象,因此乘法和幂运算符都变成了矩阵运算,于是上面计算的是矩阵a和其逆矩阵的乘积,结果是一个单位矩阵。&br&矩阵的乘积可以使用dot函数进行计算。对于二维数组,它计算的是矩阵乘积,对于一维数组,它计算的是其点积。当需要将一维数组当作列矢量或者行矢量进行矩阵运算时,推荐先使用reshape函数将一维数组转换为二维数组:&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-4ba81104a77aaca4c1f5af_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&203& data-rawheight=&161& class=&content_image& width=&203&&&/figure&&p&&br&除了dot计算乘积之外,NumPy还提供了inner和outer等多种计算乘积的函数。这些函数计算乘积的方式不同,尤其是当对于多维数组的时候,更容易搞混。&br&&/p&&ul&&li&&b&dot&/b& : 对于两个一维的数组,计算的是这两个数组对应下标元素的乘积和(数学上称之为内积);对于二维数组,计算的是两个数组的矩阵乘积;对于多维数组,它的通用 计算公式如下,即结果数组中的每个元素都是:数组a的最后一维上的所有元素与数组b的倒数第二位上的所有元素的乘积和:&/li&&/ul&&figure&&img src=&https://pic2.zhimg.com/v2-d04a8e5aa51c1fa6f158_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&420& data-rawheight=&398& class=&content_image& width=&420&&&/figure&&p&&br&&b&inner&/b& : 和dot乘积一样,对于两个一维数组,计算的是这两个数组对应下标元素的乘积和;对于多维数组,它计算的结果数组中的每个元素都是:数组a和b的最后一维的内积,因此数组a和b的最后一维的长度必须相同:&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-20de4c592c5f89bf74c939a_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&336& data-rawheight=&318& class=&content_image& width=&336&&&/figure&&p&&br&&b&outer&/b& : 只按照一维数组进行计算,如果传入参数是多维数组,则先将此数组展平为一维数组之后再进行运算。outer乘积计算的列向量和行向量的矩阵乘积:&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-bb9a8f239e3a405ed1afbbc22c99c43b_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&245& data-rawheight=&99& class=&content_image& width=&245&&&/figure&&p&&br&矩阵中更高级的一些运算可以在NumPy的线性代数子库linalg中找到。例如inv函数计算逆矩阵,solve函数可以求解多元一次方程组。下面是solve函数的一个例子:&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-43b3c4ca1258eaefb621dca_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&244& data-rawheight=&121& class=&content_image& width=&244&&&/figure&&p&&br&solve函数有两个参数a和b。a是一个N*N的二维数组,而b是一个长度为N的一维数组,solve函数找到一个长度为N的一维数组x,使得a和x的矩阵乘积正好等于b,数组x就是多元一次方程组的解。&br&有关线性代数方面的内容将在今后的章节中详细介绍。&br&&b&2.4 文件存取&/b&&br&NumPy提供了多种文件操作函数方便我们存取数组内容。文件存取的格式分为两类:二进制和文本。而二进制格式的文件又分为NumPy专用的格式化二进制类型和无格式类型。&br&使用数组的方法函数tofile可以方便地将数组中数据以二进制的格式写进文件。tofile输出的数据没有格式,因此用numpy.fromfile读回来的时候需要自己格式化数据:&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-31bc494392edd6c42287_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&434& data-rawheight=&472& class=&origin_image zh-lightbox-thumb& width=&434& data-original=&https://pic3.zhimg.com/v2-31bc494392edd6c42287_r.jpg&&&/figure&&p&&br&从上面的例子可以看出,需要在读入的时候设置正确的dtype和shape才能保证数据一致。并且tofile函数不管数组的排列顺序是C语言格式的还是Fortran语言格式的,统一使用C语言格式输出。&br&此外如果fromfile和tofile函数调用时指定了sep关键字参数的话,数组将以文本格式输入输出。&br&numpy.load和numpy.save函数以NumPy专用的二进制类型保存数据,这两个函数会自动处理元素类型和shape等信息,使用它们读写数组就方便多了,但是numpy.save输出的文件很难和其它语言编写的程序读入:&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-958e1baa97a1d_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&206& data-rawheight=&142& class=&content_image& width=&206&&&/figure&&p&&br&如果你想将多个数组保存到一个文件中的话,可以使用numpy.savez函数。savez函数的第一个参数是文件名,其后的参数都是需要保存的数 组,也可以使用关键字参数为数组起一个名字,非关键字参数传递的数组会自动起名为arr_0, arr_1, ...。savez函数输出的是一个压缩文件(扩展名为npz),其中每个文件都是一个save函数保存的npy文件,文件名对应于数组名。load函数 自动识别npz文件,并且返回一个类似于字典的对象,可以通过数组名作为关键字获取数组的内容:&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-e2ff63a8a47fdd6a7d8f9a3a67a55212_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&459& data-rawheight=&305& class=&origin_image zh-lightbox-thumb& width=&459& data-original=&https://pic4.zhimg.com/v2-e2ff63a8a47fdd6a7d8f9a3a67a55212_r.jpg&&&/figure&&p&&br&如果你用解压软件打开result.npz文件的话,会发现其中有三个文件:arr_0.npy, arr_1.npy, sin_array.npy,其中分别保存着数组a, b, c的内容。&br&使用numpy.savetxt和numpy.loadtxt可以读写1维和2维的数组:&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-5b4cf50e565d2a871d48f_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&504& data-rawheight=&294& class=&origin_image zh-lightbox-thumb& width=&504& data-original=&https://pic4.zhimg.com/v2-5b4cf50e565d2a871d48f_r.jpg&&&/figure&&p&&br&文件名和文件对象&br&本节介绍所举的例子都是传递的文件名,也可以传递已经打开的文件对象,例如对于load和save函数来说,如果使用文件对象的话,可以将多个数组储存到一个npy文件中:&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-a00dd01e1a03e39a180b_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&298& data-rawheight=&336& class=&content_image& width=&298&&&/figure&&p&&br&&b&一、数组方法&/b&&br&创建数组:arange()创建一维数组;array()创建一维或多维数组,其参数是类似于数组的对象,如列表等&br&创建数组:np.zeros((2,3)),或者np.ones((2,3)),参数是一个元组分别表示行数和列数&br&对应元素相乘,a * b,得到一个新的矩阵,形状要一致;但是允许a是向量而b是矩阵,a的列数必须等于b的列数,a与每个行向量点乘&br&+ - / 与 * 的运算规则相同。&br&数学上定义的矩阵乘法 np.dot(a, b)。如果形状不匹配会报错;但是允许允许a和b都是向量,返回两个向量的内积。只要有一个参数不是向量,就应用矩阵乘法。&br&(PS:总之就是,向量很特殊,在运算中可以自由转置而不会出错,运算的返回值如果维度为1,也一律用行向量[]表示)&br&读取数组元素:如a[0],a[0,0]&br&数组变形:如b=a.reshape(2,3,4)将得到原数组变为2*3*4的三维数组后的数组;或是a.shape=(2,3,4)或a.resize(2,3,4)直接改变数组a的形状&br&数组组合:水平组合hstack((a,b))或concatenate((a,b),axis=1);垂直组合vstack((a,b))或concatenate((a,b),axis=0);深度组合dstack((a,b))&br&数组分割(与数组组合相反):分别有hsplit,vsplit,dsplit,split(split与concatenate相对应)&br&将np数组变为py列表:a.tolist()&br&数组排序(小到大):列排列np.msort(a),行排列np.sort(a),np.argsort(a)排序后返回下标&br&复数排序:np.sort_complex(a)按先实部后虚部排序&br&数组的插入:np.searchsorted(a,b)将b插入原有序数组a,并返回插入元素的索引值&br&类型转换:如a.astype(int),np的数据类型比py丰富,且每种类型都有转换方法&br&条件查找,返回满足条件的数组元素的索引值:np.where(条件)&br&条件查找,返回下标:np.argwhere(条件)&br&条件查找,返回满足条件的数组元素:np.extract([条件],a)&br&根据b中元素作为索引,查找a中对应元素:np.take(a,b)一维&br&数组中最小最大元素的索引:np.argmin(a),np.argmax(a)&br&多个数组的对应位置上元素大小的比较:np.maximum(a,b,c,…..)返回每个索引位置上的最大值,np.minimum(…….)相反&br&将a中元素都置为b:a.fill(b)&br&每个数组元素的指数:np.exp(a)&br&生成等差行向量:如np.linspace(1,6,10)则得到1到6之间的均匀分布,总共返回10个数&br&求余:np.mod(a,n)相当于a%n,np.fmod(a,n)仍为求余且余数的正负由a决定&br&计算平均值:np.mean(a)&br&计算最大值:amax(a, axis=None, out=None, keepdims=False) 。Return the maximum of an array or maximum along an axis.&br&计算加权平均值:np.average(a,b),其中b是权重&br&计算数组的极差:np.pth(a)=max(a)-min(a)&br&计算方差(总体方差):np.var(a)&br&标准差:np.std(a)&br&算术平方根,a为浮点数类型:np.sqrt(a)&br&对数:np.log(a)&br&修剪数组,将数组中小于x的数均换为x,大于y的数均换为y:a.clip(x,y)&br&所有数组元素乘积:a.prod()&br&数组元素的累积乘积:a.cumprod()&br&数组元素的符号:np.sign(a),返回数组中各元素的正负符号,用1和-1表示&br&数组元素分类:np.piecewise(a,[条件],[返回值]),分段给定取值,根据判断条件给元素分类,并返回设定的返回值。&br&判断两数组是否相等: np.array_equal(a,b)&br&判断数组元素是否为实数: np.isreal(a)&br&去除数组中首尾为0的元素:np.trim_zeros(a)&br&对浮点数取整,但不改变浮点数类型:np.rint(a)&br&&b&二、数组属性&/b&&br&1.获取数组每一维度的大小:a.shape&br&2.获取数组维度:a.ndim&br&3.元素个数:a.size&br&4.数组元素在内存中的字节数:a.itemsize&br&5.数组字节数:a.nbytes==a.size*a.itemsize&br&6.数组元素覆盖:a.flat=1,则a中数组元素都被1覆盖&br&7.数组转置:a.T&br&不能求逆、求协方差、迹等,不适用于复杂科学计算,可以将array转换成matrix。&br&&b&三、矩阵方法&/b&&br&创建矩阵:np.mat(‘…’)通过字符串格式创建,np.mat(a)通过array数组创建,也可用matrix或bmat函数创建&br&创建复合矩阵:np.bmat(‘A B’,’AB’),用A和B创建复合矩阵AB(字符串格式)&br&创建n*n维单位矩阵:np.eye(n)&br&矩阵的转置:A.T&br&矩阵的逆矩阵:A.I&br&计算协方差矩阵:np.cov(x),np.cov(x,y)&br&计算矩阵的迹(对角线元素和):a.trace()&br&相关系数:np.corrcoef(x,y)&br&给出对角线元素:a.diagonal()&br&&b&四、线性代数&/b&&br&估计线性模型中的系数:a=np.linalg.lstsq(x,b),有b=a*x&br&求方阵的逆矩阵:np.linalg.inv(A)&br&求广义逆矩阵:np.linalg.pinv(A)&br&求矩阵的行列式:np.linalg.det(A)&br&解形如AX=b的线性方程组:np.linalg.solve(A,b)&br&求矩阵的特征值:np.linalg.eigvals(A)&br&求特征值和特征向量:np.linalg.eig(A)&br&Svd分解:np.linalg.svd(A)&br&&b&五、概率分布&/b&&br&产生二项分布的随机数:np.random.binomial(n,p,size=…),其中n,p,size分别是每轮试验次数、概率、轮数&br&产生超几何分布随机数:np.random.hypergeometric(n1,n2,n,size=…),其中参数意义分别是物件1总量、物件2总量、每次采样数、试验次数&br&产生N个正态分布的随机数:np.random.normal(均值,标准差,N)&br&产生N个对数正态分布的

我要回帖

更多关于 java判断输入是否为整数 的文章

 

随机推荐