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