《R语言编程艺术》——2.5 使用all()和any()
2.5 使用all()和any()
any() 和all() 函數(shù)非常方便快捷,它們分別報(bào)告其參數(shù)是否至少有一個(gè)或全部為TRUE。
any()函數(shù)判斷這些值是否至少一個(gè)為TURE。all()函數(shù)的功能類似,它判斷這些值是否全部為TRUE。
2.5.1 擴(kuò)展案例:尋找連續(xù)出現(xiàn)1 的游程
假設(shè)一個(gè)向量由若干0 和1 構(gòu)成,我們想找出其中連續(xù)出現(xiàn)1 的游程。例如,對(duì)于向量(1,0,0,1,1,1,0,1,1),從它第4索引處開始有長度為3的游程,而長度為2的游程分別始于第4,第5和第8索引的位置。因此,用語句findruns(c(1,0,0,1,1,1,0,1,1),2)調(diào)用下面展示的函數(shù),返回結(jié)果(4,5,8)。代碼如下:
第五行,我們需要判斷從x[i]開始的連續(xù)k個(gè)值,即x[i],x[i+1],...,x[i+k-1]的值,是否全部為1。表達(dá)式x[i:(i+k-1)]語句給出了上述子向量的值,然后使用all()函數(shù)檢驗(yàn)它是否是一個(gè)游程。
我們對(duì)它進(jìn)行一下測試:
盡管前面的代碼中使用all()比較好,但建立向量runs的過程并不理想。向量的內(nèi)存分配過程比較耗時(shí),由于調(diào)用c(runs,i)時(shí)給新的向量分配了內(nèi)存空間,每次執(zhí)行時(shí)都會(huì)減慢代碼的運(yùn)行速度。(這與新向量賦值給runs無關(guān),我們?nèi)匀唤o向量分配了內(nèi)存空間。)
在較短的循環(huán)中,這樣做可能沒問題,但當(dāng)應(yīng)用程序的運(yùn)行性能受到重點(diǎn)關(guān)注時(shí),這里有更好的方法。
一種替代方法是預(yù)先分配的內(nèi)存空間,像這樣:
在第3行,我們給一個(gè)長度為n的向量分配了內(nèi)存空間。這意味著在執(zhí)行循環(huán)的過程中,可以避免分配新的內(nèi)存。第8行代碼做的只是填充runs。在退出函數(shù)之前,我們?cè)诘?2行重新定義runs,來刪除該向量中沒用的部分。
這種方法更好,第一版代碼可能會(huì)有很多次內(nèi)存分配,而第二版代碼將之減少為兩次。如果我們確實(shí)需要提高速度,可能考慮使用C語言重新編碼,這會(huì)在第14章中討論。
2.5.2 擴(kuò)展案例:預(yù)測離散值時(shí)間序列
假設(shè)我們觀察到取值為0或1的數(shù)據(jù),每個(gè)時(shí)刻一個(gè)值。為了了解具體應(yīng)用,假設(shè)這是每天的天氣數(shù)據(jù):1代表有雨,0代表沒有雨。假設(shè)已經(jīng)知道最近幾天是否下雨,我們希望預(yù)測明天是否會(huì)下雨。具體而言,對(duì)于某個(gè)k值,我們會(huì)根據(jù)最近k天的天氣記錄來預(yù)測明天的天氣。我們將使用“過半數(shù)規(guī)則”(majority rule:):如果在最近k期里1的數(shù)量大于等于k/2,那么預(yù)測下一個(gè)值為1,否則,預(yù)測下一個(gè)值為0。例如如果k=3,最近三期的數(shù)據(jù)為1、0、1,則預(yù)測下一期值為1。
但是,我們應(yīng)該如何選擇k?顯然,如果選擇的值太小,則給我們用以預(yù)測的樣本量太小。如果取值過大,導(dǎo)致我們使用過于早期的數(shù)據(jù),而這些數(shù)據(jù)只有很少或根本沒有預(yù)測價(jià)值。
一個(gè)解決方案是針對(duì)已知的數(shù)據(jù)(稱為訓(xùn)練集),變換不同的k值,看看預(yù)測效果如何。
在天氣的例子中,假設(shè)我們有500天的數(shù)據(jù),假設(shè)我們考慮使用k=3。為了評(píng)價(jià)k值的預(yù)測能力,我們基于前三天的數(shù)據(jù)來“預(yù)測”每天的數(shù)據(jù),然后將預(yù)測值與已知值進(jìn)行對(duì)比。以此類推,對(duì)于k=1、k=2、k=4,我們做同樣的事情,直到k值足夠大。然后,我們使用訓(xùn)練數(shù)據(jù)中表現(xiàn)最好的k值,用于未來的預(yù)測。
那么我們?nèi)绾尉帉慠代碼?這里有一個(gè)簡單的方法:
這段代碼的核心在第7行。此處要預(yù)測第i+k天的值(預(yù)測結(jié)果保存在pred[i]),利用的是之前k天的值,也即第i天,……,第i+k-1天的值。因此,我們需要算出這些天中1的個(gè)數(shù)。由于我們處理的是0-1數(shù)據(jù),1的數(shù)量可以簡單地使用這些天x[j]的總和,它可以很方便地用以下方法獲取:
使用sum()函數(shù)和向量索引使得計(jì)算更簡捷,避免了循環(huán),因此它更簡單更快速。這是R語言典型的用法。
第9行的表達(dá)式也是同樣的道理:
在這里,pred包含預(yù)測值,而x[(k+1):n]是這些天的實(shí)際值。前者減去后者,得到的值要么為0,要么為1,或-1。在這里,1或-1對(duì)應(yīng)兩個(gè)方向的預(yù)測誤差,即當(dāng)真實(shí)值為1時(shí)預(yù)測值為0,或者真實(shí)值為0時(shí)預(yù)測為1。再用abs()函數(shù)求出絕對(duì)值,得到0和1的序列,后者表示預(yù)測有誤差。
這樣,我們就能知道哪些天的預(yù)測有誤差,然后使用mean()來計(jì)算錯(cuò)誤率,在這里我們應(yīng)用了這一數(shù)學(xué)原理:即0-1數(shù)據(jù)的均值是1的比例。這是R語言的一個(gè)常見技巧。
上述preda()的編碼是相當(dāng)直截了當(dāng)?shù)?#xff0c;它的優(yōu)點(diǎn)是簡單和緊湊。然而,它可能很慢。我們可以嘗試用向量化循環(huán)來加快速度,正如2.6節(jié)所討論的那樣。然而在這里它不能解決加速的主要障礙,即這些代碼中所有的重復(fù)計(jì)算都不能避免。在循環(huán)中對(duì)于i的相鄰兩個(gè)取值,調(diào)用sum()函數(shù)求和的向量只相差兩個(gè)元素。這會(huì)減慢速度,除非k值非常小。
所以,我們重寫代碼,計(jì)算過程中利用上一步計(jì)算的結(jié)果。在循環(huán)的每一次迭代中,將更新前一次得到的總和,而不是從頭開始計(jì)算新的總和。
關(guān)鍵在第9行。在這里從總和sm里減去最早的元素x[i-1],再加上新的元素(x[i+k-1]),從而更新sm。
另一種方法是使用R函數(shù)cumsum(),它能計(jì)算向量的累積和(cumulative sums)。這里是一個(gè)例子:
在這里,y的累加和是5=5,5+2=7,5 + 2 + (-3) = 4,5 + 2 + (-3) + 8 = 12,這些值由cumsum()返回。
在上面的例子里,建議用cumsum()的差值替代preda()中的表達(dá)式sum(x[i:(i+(k-1))。
在求x中連續(xù)k個(gè)元素(稱為窗口)之和的時(shí)候,沒有像下面這樣使用sum()函數(shù):
這是為了保證在i=1時(shí)能計(jì)算出正確的值。
predb()函數(shù)里每次循環(huán)迭代要做兩次減法運(yùn)算,對(duì)predc()來說只需要做一次。
總結(jié)
以上是生活随笔為你收集整理的《R语言编程艺术》——2.5 使用all()和any()的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 大数据时代 我们还有隐私吗?
- 下一篇: 万物互联时代 能效管理是怎样一个玩法?