摘要:將每一行作為返回,其中是每行中的列名。對(duì)于每一行,都會(huì)生成一個(gè)對(duì)象,其中包含和列中的值。它返回一個(gè)迭代器,是迭代結(jié)果都為的情況。深度解析至此全劇終。
簡(jiǎn)單實(shí)戰(zhàn)
大家好,我又來(lái)了,在經(jīng)過(guò)之前兩篇文章的介紹后相信大家對(duì)itertools的一些常見(jiàn)的好用的方法有了一個(gè)大致的了解,我自己在學(xué)完之后仿照別人的例子進(jìn)行了真實(shí)場(chǎng)景下的模擬練習(xí),今天和大家一起分享,有很多部分還可以?xún)?yōu)化,希望有更好主意和建議的朋友們可以留言哈,讓我們一起進(jìn)步
實(shí)戰(zhàn):分析標(biāo)準(zhǔn)普爾500指數(shù) 數(shù)據(jù)源及目標(biāo)在這個(gè)例子中,我們首先嘗試使用itertools來(lái)操作大型數(shù)據(jù)集:標(biāo)準(zhǔn)普爾500指數(shù)的歷史每日價(jià)格數(shù)據(jù)。 我會(huì)在這個(gè)部分的最后附上下載鏈接和py文件,這里的數(shù)據(jù)源來(lái)自雅虎財(cái)經(jīng)
目標(biāo): 找到標(biāo)準(zhǔn)普爾500指數(shù)的單日最大收益,最大損失(百分比),和最長(zhǎng)的增長(zhǎng)周期
首先我們手上得到了 SP500.csv ,讓我們對(duì)數(shù)據(jù)有個(gè)大概的印象,前十行的數(shù)據(jù)如下:
Date,Open,High,Low,Close,Adj Close,Volume 1950-01-03,16.660000,16.660000,16.660000,16.660000,16.660000,1260000 1950-01-04,16.850000,16.850000,16.850000,16.850000,16.850000,1890000 1950-01-05,16.930000,16.930000,16.930000,16.930000,16.930000,2550000 1950-01-06,16.980000,16.980000,16.980000,16.980000,16.980000,2010000 1950-01-09,17.080000,17.080000,17.080000,17.080000,17.080000,2520000 1950-01-10,17.030001,17.030001,17.030001,17.030001,17.030001,2160000 1950-01-11,17.090000,17.090000,17.090000,17.090000,17.090000,2630000 1950-01-12,16.760000,16.760000,16.760000,16.760000,16.760000,2970000 1950-01-13,16.670000,16.670000,16.670000,16.670000,16.670000,3330000
為了實(shí)現(xiàn)目標(biāo),具體思路如下:
讀取csv文件,并利用 Adj Close這一列轉(zhuǎn)換為每日百分比變化的序列,代表收益,命名為gain
找到gain這一序列中的最大值和最小值,并且找到對(duì)應(yīng)的日期,當(dāng)然,有可能會(huì)出現(xiàn)對(duì)應(yīng)多個(gè)日期的情況,我們這里選取日期最近的就好。
定義一個(gè)sequence叫做growth_streaks,其中包含了所有 gain中出現(xiàn)的連續(xù)為正值的元素組成的tuple,我們要找到這些tuples中長(zhǎng)度最長(zhǎng)的一個(gè),從而定位其對(duì)應(yīng)的開(kāi)始時(shí)間和結(jié)束時(shí)間,當(dāng)然這里也是一樣,有可能出現(xiàn)最大長(zhǎng)度一樣的的情況,這種情況下,我們還是選擇日期最近的。
這里有關(guān)百分比的計(jì)算公式如下:
分步實(shí)現(xiàn)首先在這里,我們會(huì)經(jīng)常處理日期,為了方便后續(xù)操作,這里我們引入collections模塊的namedtuple來(lái)實(shí)現(xiàn)對(duì)日期的相關(guān)操作:
from collections import namedtuple class DataPoint(namedtuple("DataPoint", ["date", "value"])): __slots__ = () def __le__(self, other): return self.value <= other.value def __lt__(self, other): return self.value < other.value def __gt__(self, other): return self.value > other.value
這里有很多小技巧,之后我會(huì)再系統(tǒng)的開(kāi)一個(gè)Python OOP筆記,會(huì)為大家都講到,這里面涉及的小知識(shí)點(diǎn)如下:
slots :這是一個(gè)節(jié)省變量?jī)?nèi)存的好東西,__slot__后面一般都是跟class中 init 方法里面用到的變量,好處在于能夠大量節(jié)省內(nèi)存
namedtuple:可以實(shí)現(xiàn)類(lèi)似屬性一樣調(diào)用tuple里面的元素,我在collections里面詳細(xì)說(shuō)過(guò),大家可以看看:Python 進(jìn)階之路 (七) 隱藏的神奇寶藏:探秘Collections
le:運(yùn)算符重載,可以得到class中一個(gè)變量的長(zhǎng)度,必須是整數(shù),也就是說(shuō)如果傳入的是list,dict,tuple,set這些一定沒(méi)有問(wèn)題,因?yàn)檫@些序列的長(zhǎng)度一定是整數(shù),這里面?zhèn)鬟f的是tuple()
lt:運(yùn)算符重載(less than ):可以實(shí)現(xiàn)利用 < 比較一個(gè)class的不同對(duì)象中的值大小的比較
gt:運(yùn)算符重載(greater than):可以實(shí)現(xiàn)利用 > 比較一個(gè)class的不同對(duì)象中的值大小的比較
下面為了喚醒大家的記憶,我這里快速舉一個(gè)有關(guān)于namedtuple,le,lt,gt的小栗子:
from collections import namedtuple class Person(namedtuple("person", ["name", "age","city","job"])): def __le__(self): return len(self) def __lt__(self,other): return self.age < other.age def __gt__(self,other): return self.age > other.age xiaobai = Person("xiaobai", 18, "paris","student") laobai = Person("Walter White",52, "albuquerque","cook") print("Infomation for first person: ", xiaobai) # 顯示全部信息 print("Age of second person is: ", laobai.age) # 根據(jù)name得到tuple的數(shù)據(jù) print(len(xiaobai)) print(xiaobai > laobai) print(xiaobai < laobai) Out: Infomation for first person: Person(name="xiaobai", age=18, city="paris",job="student") Age of second person is: 52 4 False True
如果大家對(duì)這個(gè)例子中的一些地方還有疑問(wèn),不用擔(dān)心,我會(huì)在下一個(gè)專(zhuān)欄Python OOP學(xué)習(xí)筆記中和大家慢慢說(shuō)的
。好的,現(xiàn)在回到剛才的實(shí)戰(zhàn):
from collections import namedtuple class DataPoint(namedtuple("DataPoint", ["date", "value"])): __slots__ = () def __le__(self, other): return self.value <= other.value def __lt__(self, other): return self.value < other.value def __gt__(self, other): return self.value > other.value
這里我們的DataPoint類(lèi)有兩個(gè)主要屬性,一個(gè)是datetime類(lèi)型的日期,一個(gè)是當(dāng)天的標(biāo)普500值
接下來(lái)讓我們讀取csv文件,并將每行中的Date和Adj Close列中的值存為DataPoint的對(duì)象,最后把所有的對(duì)象組合為一個(gè)sequence序列:
import csv from datetime import datetime def read_prices(csvfile, _strptime=datetime.strptime): with open(csvfile) as infile: reader = csv.DictReader(infile) for row in reader: yield DataPoint(date=_strptime(row["Date"], "%Y-%m-%d").date(), value=float(row["Adj Close"])) prices = tuple(read_prices("SP500.csv"))
read_prices()生成器打開(kāi) SP500.csv 并使用 csv.DictReader()讀取數(shù)據(jù)的每一行。DictReader()將每一行作為 OrderedDict 返回,其中key是每行中的列名。
對(duì)于每一行,read_prices()都會(huì)生成一個(gè)DataPoint對(duì)象,其中包含“Date”和“Adj Close”列中的值。 最后,完整的數(shù)據(jù)點(diǎn)序列作為元組提交給內(nèi)存并存儲(chǔ)在prices變量中
Ps: Ordereddict是我在collections中漏掉的知識(shí)點(diǎn),我馬上會(huì)補(bǔ)上,大家可以隨時(shí)收藏Python 進(jìn)階之路 (七) 隱藏的神奇寶藏:探秘Collections,我會(huì)繼續(xù)更新
接下來(lái)我們要把prices這個(gè)轉(zhuǎn)變?yōu)楸磉_(dá)每日價(jià)格變化百分比的序列,利用的公式就是剛才提到的,如果忘了的朋友可以往回翻~
gains = tuple(DataPoint(day.date, 100*(day.value/prev_day.value - 1.)) for day, prev_day in zip(prices[1:], prices))
為了得到標(biāo)普500單日最大漲幅,我們可以用一下方法:
max_gain = DataPoint(None, 0) for data_point in gains: max_gain = max(data_point, max_gain) print(max_gain) # DataPoint(date="2008-10-28", value=11.58)
我們可以把這個(gè)方法用之前提到過(guò)的reduce簡(jiǎn)化一下:
import functools as ft max_gain = ft.reduce(max, gains) print(max_gain) # DataPoint(date="2008-10-28", value=11.58)
這里有關(guān)reduce 和 lambda的用法,我們可以通過(guò)一個(gè)小栗子來(lái)回憶一下:
import functools as ft x = ft.reduce(lambda x,y:x+y,[1, 2, 3, 4, 5]) print(x) Out: 15
當(dāng)然,如果求和在實(shí)際場(chǎng)景直接用sum就好,這里只是為了讓大家有個(gè)印象,如果回憶不起來(lái)的老鐵們也沒(méi)有關(guān)系,輕輕點(diǎn)擊以下鏈接立刻重溫:
Python 進(jìn)階之路 (五) map, filter, reduce, zip 一網(wǎng)打盡
Python 進(jìn)階之路 (六) 九淺一深 lambda,陳獨(dú)秀你給我坐下!
好了,書(shū)規(guī)正傳,我們發(fā)現(xiàn)用reduce改進(jìn)了for循環(huán)后得到了同樣的結(jié)果,單日最大漲幅的日期也一樣,但是這里需要注意的是reduce和剛才的for循環(huán)完全不是一回事
我們可以想象一下,假如CSV文件中的數(shù)據(jù)每天都是跌的話(huà)。 max_gain最后到底是多少?
在 for 循環(huán)中,首先設(shè)置max_gain = DataPoint(None,0),因此如果沒(méi)有漲幅,則最終的max_gain值將是此空 DataPoint 對(duì)象。但是,reduce()解決方案會(huì)返回最小的單日跌幅,這不是我們想要的,可能會(huì)引入一個(gè)難以找到的bug
這就是itertools可以幫助到我們的地方。 itertools.filterfalse()函數(shù)有兩個(gè)參數(shù):一個(gè)返回True或False的函數(shù),和一個(gè)可迭代的輸入。它返回一個(gè)迭代器,是迭代結(jié)果都為False的情況。這里是個(gè)小栗子:
import itertools as it only_positives = it.filterfalse(lambda x: x <= 0, [0, 1, -1, 2, -2]) print(list(only_positives)) Out:[1, 2]
所以現(xiàn)在我們可以用 itertools.filterfalse()去除掉gains中那些小于0或者為負(fù)數(shù)的值,這樣reduce會(huì)僅僅作用在我們想要的正收益上:
max_gain = ft.reduce(max, it.filterfalse(lambda p: p <= 0, gains))
這里我們默認(rèn)為gains中一定存在大于0的值,這也是事實(shí),但是如果假設(shè)gains中沒(méi)有的話(huà),我們會(huì)報(bào)錯(cuò),因此在使用itertools.filterfalse()的實(shí)際場(chǎng)景中要注意到這一點(diǎn)。
針對(duì)這種情況,可能你想到的應(yīng)對(duì)方案是在合適的情況下添加TryExpect捕獲錯(cuò)誤,但是reduce有個(gè)更好的解決方案,reuce里面可以傳遞第三個(gè)參數(shù),用做reduce返回結(jié)果不存在時(shí)的默認(rèn)值,這一點(diǎn)和字典的get方法有異曲同工之妙,如果對(duì)get有疑問(wèn)的朋友可以回顧我之前的文章:Python 進(jìn)階之路 (二) Dict 進(jìn)階寶典,初二快樂(lè)!,還是看一個(gè)小栗子:
>>> ft.reduce(max, it.filterfalse(lambda x: x <= 0, [-1, -2, -3]), 0) 0
這回很好理解了,因此我們應(yīng)用到我們標(biāo)準(zhǔn)普爾指數(shù)的實(shí)戰(zhàn)上:
zdp = DataPoint(None, 0) # zero DataPoint max_gain = ft.reduce(max, it.filterfalse(lambda p: p.value <= 0, diffs), zdp)
同理,對(duì)于標(biāo)普500單日最大跌幅我們也照貓畫(huà)虎:
max_loss = ft.reduce(min, it.filterfalse(lambda p: p.value > 0, gains), zdp) print(max_loss) # DataPoint(date="2018-02-08", value=-20.47)
根據(jù)我們的數(shù)據(jù)源是2018年2月8號(hào)那一天,我沒(méi)有谷歌查詢(xún)那一天發(fā)生了什么,大家感興趣可以看看哈,但是應(yīng)該是沒(méi)有問(wèn)題的,因?yàn)閿?shù)據(jù)源來(lái)自雅虎財(cái)經(jīng)
現(xiàn)在我們已經(jīng)得到了標(biāo)普500歷史上的單日最大漲跌的日期,我們接下來(lái)要找到它的最長(zhǎng)時(shí)間段,其實(shí)這個(gè)問(wèn)題等同于在gains序列中找到最長(zhǎng)的連續(xù)為正數(shù)的點(diǎn)的集合,itertools.takewhile()和itertools.dropwhile()函數(shù)非常適合處理這種情況。
itertools.takewhile()接受兩個(gè)參數(shù),一個(gè)為判斷的條件,一個(gè)為可迭代的序列,會(huì)返回第一個(gè)判斷結(jié)果為False時(shí)之前的迭代過(guò)的所有元素,下面的小栗子很好的解釋了這一點(diǎn)
it.takewhile(lambda x: x < 3, [0, 1, 2, 3, 4]) # 0, 1, 2
itertools.dropwhile() 則恰恰相反:
it.dropwhile(lambda x: x < 3, [0, 1, 2, 3, 4]) # 3, 4
因此我們可以創(chuàng)建一下方法來(lái)實(shí)現(xiàn)在gains中找到連續(xù)為正數(shù)的序列:
def consecutive_positives(sequence, zero=0): def _consecutives(): for itr in it.repeat(iter(sequence)): yield tuple(it.takewhile(lambda p: p > zero, it.dropwhile(lambda p: p <= zero, itr))) return it.takewhile(lambda t: len(t), _consecutives()) growth_streaks = consecutive_positives(gains, zero=DataPoint(None, 0)) longest_streak = ft.reduce(lambda x, y: x if len(x) > len(y) else y, growth_streaks)
最后讓我們看一下完整的代碼:
from collections import namedtuple import csv from datetime import datetime import itertools as it import functools as ft class DataPoint(namedtuple("DataPoint", ["date", "value"])): __slots__ = () def __le__(self, other): return self.value <= other.value def __lt__(self, other): return self.value < other.value def __gt__(self, other): return self.value > other.value def consecutive_positives(sequence, zero=0): def _consecutives(): for itr in it.repeat(iter(sequence)): yield tuple(it.takewhile(lambda p: p > zero, it.dropwhile(lambda p: p <= zero, itr))) return it.takewhile(lambda t: len(t), _consecutives()) def read_prices(csvfile, _strptime=datetime.strptime): with open(csvfile) as infile: reader = csv.DictReader(infile) for row in reader: yield DataPoint(date=_strptime(row["Date"], "%Y-%m-%d").date(), value=float(row["Adj Close"])) # Read prices and calculate daily percent change. prices = tuple(read_prices("SP500.csv")) gains = tuple(DataPoint(day.date, 100*(day.value/prev_day.value - 1.)) for day, prev_day in zip(prices[1:], prices)) # Find maximum daily gain/loss. zdp = DataPoint(None, 0) # zero DataPoint max_gain = ft.reduce(max, it.filterfalse(lambda p: p.value <= zdp, gains)) max_loss = ft.reduce(min, it.filterfalse(lambda p: p.value > zdp, gains), zdp) # Find longest growth streak. growth_streaks = consecutive_positives(gains, zero=DataPoint(None, 0)) longest_streak = ft.reduce(lambda x, y: x if len(x) > len(y) else y, growth_streaks) # Display results. print("Max gain: {1:.2f}% on {0}".format(*max_gain)) print("Max loss: {1:.2f}% on {0}".format(*max_loss)) print("Longest growth streak: {num_days} days ({first} to {last})".format( num_days=len(longest_streak), first=longest_streak[0].date, last=longest_streak[-1].date ))
最終結(jié)果如下:
Max gain: 11.58% on 2008-10-13 Max loss: -20.47% on 1987-10-19 Longest growth streak: 14 days (1971-03-26 to 1971-04-15)
數(shù)據(jù)源可以點(diǎn)擊這里下載
總結(jié)這次我為大家梳理一個(gè)利用itertools進(jìn)行了簡(jiǎn)單實(shí)戰(zhàn)的小栗子,這里我們旨在多深入了解itertools,但是真實(shí)的生活中,遇到這種問(wèn)題,哪有這么麻煩,一個(gè)pandas包就搞定了,我以后會(huì)和大家分享和pandas有關(guān)的知識(shí),這一次接連三期的itertools總結(jié)希望大家喜歡。
itertools深度解析至此全劇終。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/43247.html
前情回顧 大家好,我又回來(lái)了。今天我會(huì)繼續(xù)和大家分享itertools這個(gè)神奇的自帶庫(kù),首先,讓我們回顧一下上一期結(jié)尾的時(shí)候我們講到的3個(gè)方法: combinations() combinations_with_replacement() permutations() 讓我們對(duì)這3個(gè)在排列組合中經(jīng)常會(huì)使用到的函數(shù)做個(gè)總結(jié) combinations() 基礎(chǔ)概念 模板:combinations...
摘要:例如,以下對(duì)兩個(gè)的相應(yīng)元素求和這個(gè)例子很好的解釋了如何構(gòu)建中所謂的迭代器代數(shù)的函數(shù)的含義。為簡(jiǎn)單起見(jiàn),假設(shè)輸入的長(zhǎng)度可被整除。接受兩個(gè)參數(shù)一個(gè)可迭代的正整數(shù)最終會(huì)在中個(gè)元素的所有組合的元組上產(chǎn)生一個(gè)迭代器。 前言 大家好,今天想和大家分享一下我的itertools學(xué)習(xí)體驗(yàn)及心得,itertools是一個(gè)Python的自帶庫(kù),內(nèi)含多種非常實(shí)用的方法,我簡(jiǎn)單學(xué)習(xí)了一下,發(fā)現(xiàn)可以大大提升工作...
摘要:與上面的操作類(lèi)似,可以使用多種運(yùn)算符和方法來(lái)更改集合的內(nèi)容。通過(guò)修改集合元素方法運(yùn)算符用法通過(guò)修改集合和作用是向集合中添加中所有不存在的元素。 Set是什么 大家好,恰逢初五迎財(cái)神,先預(yù)祝大家新年財(cái)源滾滾!!在上一期詳解tuple元組的用法后,今天我們來(lái)看Python里面最后一種常見(jiàn)的數(shù)據(jù)類(lèi)型:集合(Set) 與dict類(lèi)似,set也是一組key的集合,但不存儲(chǔ)value。由于key不...
摘要:什么是推導(dǎo)式大家好,今天為大家?guī)?lái)問(wèn)我最喜歡的推導(dǎo)式使用指南,讓我們先來(lái)看看定義推導(dǎo)式是的一種獨(dú)有特性,推導(dǎo)式是可以從一個(gè)數(shù)據(jù)序列構(gòu)建另一個(gè)新的數(shù)據(jù)序列的結(jié)構(gòu)體。 什么是推導(dǎo)式 大家好,今天為大家?guī)?lái)問(wèn)我最喜歡的Python推導(dǎo)式使用指南,讓我們先來(lái)看看定義~ 推導(dǎo)式(comprehensions)是Python的一種獨(dú)有特性,推導(dǎo)式是可以從一個(gè)數(shù)據(jù)序列構(gòu)建另一個(gè)新的數(shù)據(jù)序列的結(jié)構(gòu)體。...
摘要:大家好,我是冰河有句話(huà)叫做投資啥都不如投資自己的回報(bào)率高。馬上就十一國(guó)慶假期了,給小伙伴們分享下,從小白程序員到大廠(chǎng)高級(jí)技術(shù)專(zhuān)家我看過(guò)哪些技術(shù)類(lèi)書(shū)籍。 大家好,我是...
閱讀 1424·2021-11-22 09:34
閱讀 1386·2021-09-22 14:57
閱讀 3418·2021-09-10 10:50
閱讀 1408·2019-08-30 15:54
閱讀 3700·2019-08-29 17:02
閱讀 3483·2019-08-29 12:54
閱讀 2623·2019-08-27 10:57
閱讀 3328·2019-08-26 12:24