CFU_Rfunc.txt 72 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982
  1. CFU_PCA<-function(data, cl = 0.98, ofiles = FALSE) {
  2. #
  3. # Computes a Principal Component Analysis (PCA/EOF) of data.
  4. #
  5. # Description:
  6. #
  7. # Returns an array of EOFs, an array of PCs, a vector of FVARs (fractions of explained variance),
  8. # an array of CORRs (correlation maps), and the threshold for the correlation coefficient based
  9. # on a two-tailed Student's t-test.
  10. # If applied, also returns ascii files for PCs and FVARs.
  11. #
  12. # Usage:
  13. #
  14. # CFU_PCA(data, sl = 0.98, ofiles = FALSE)
  15. #
  16. # Arguments:
  17. #
  18. # data: Array containing the anomalies field for the analysis
  19. #
  20. # cl: Value of the confidence level for the correlation map.
  21. # It is based on a two-tailed Student's t-test.
  22. # Default is cl=0.98 (98% confidence level; 2% significance level).
  23. #
  24. # ofiles: Logical.
  25. #
  26. # Output:
  27. #
  28. # $PCs: Array of principal components (nt, nm)
  29. # $FVARs: Vector of fractions of explained variance (nm)
  30. # $EOFs: Array of empirical orthogonal functions (c(dim), nm)
  31. # $CORRs: Array of correlation maps associated with EOFs (c(dim), nm)
  32. # $Rtest: Value of the statistically significant corr.coefficient at cl
  33. #
  34. # Author:
  35. #
  36. # Javier Garcia-Serrano <jgarcia@ic3.cat> 17 September 2010
  37. # Modified by jgarcia@ic3.cat (29 August 2011): The calculation is now done by a
  38. # singular value decomposition of the data matrix (prcomp), not by using 'eigen'
  39. # on the covariance matrix (princomp).
  40. #
  41. d=dim(data)
  42. if (length(d) == 3){
  43. nlon=d[1]; nlat=d[2]; ns=nlon*nlat; nt=d[3]
  44. } else {
  45. nlon=1; nlat=d[1]; ns=nlon*nlat; nt=d[2]
  46. }
  47. M=array(dim=c(ns,nt))
  48. for (i in 1:nlat){
  49. M[(nlon*(i-1)+1):(nlon*(i-1)+nlon),]=data[,i,]
  50. }
  51. not=is.na(M[,1])
  52. yes=array(dim=c(ns))
  53. counter=0
  54. for (i in 1:ns){
  55. if (not[i]=='FALSE'){counter=counter+1; yes[counter]=i}
  56. }
  57. yes=yes[1:counter]
  58. Myes=M[yes,] #(ns_with_data,nt)
  59. #covariance matrix
  60. #covM=(Myes%*%t(Myes))/(nt-1)
  61. #PCA analysis
  62. #PCA=princomp(covmat=covM)
  63. PCA=prcomp(t(Myes))
  64. #outputs
  65. nm=10 #number of modes to be retained (aprox 80% of FVAR)
  66. std=PCA$sdev
  67. lambda=std*std; fvar=lambda/sum(lambda)
  68. fvar=round(fvar,digits=4); fvar=fvar[1:nm]*100
  69. #U=PCA$loadings #(ns_with_data,ns_with_data)
  70. U=PCA$rotation
  71. Un=U[,1:nm]
  72. PC=t(Myes)%*%Un #(nt,nm)
  73. PCstd=array(dim=c(nt,nm))
  74. for (i in 1:nm){
  75. S=sd(PC[,i]); PCstd[,i]=PC[,i]/S
  76. }
  77. EOFyes=(Myes%*%PCstd)/nt #(ns_with_data,nm)
  78. eof=array(dim=c(ns,nm))
  79. eof[yes,]=EOFyes
  80. EOF=array(dim=c(nlon,nlat,nm))
  81. for (i in 1:nlat){
  82. EOF[,i,]=eof[(nlon*(i-1)+1):(nlon*(i-1)+nlon),]
  83. }
  84. CORRyes=array(dim=c(counter,nm))
  85. for (i in 1:nm){
  86. for (j in 1:counter){
  87. CORRyes[j,i]=cor(Myes[j,],PCstd[,i])
  88. }
  89. }
  90. corr=array(dim=c(ns,nm))
  91. corr[yes,]=CORRyes
  92. CORR=array(dim=c(nlon,nlat,nm))
  93. for (i in 1:nlat){
  94. CORR[,i,]=corr[(nlon*(i-1)+1):(nlon*(i-1)+nlon),]
  95. }
  96. #t-Test
  97. cl=cl; cl2=(1-cl)/2
  98. t=qt(cl+cl2, nt-2); R=sqrt((t*t)/((t*t)+nt-2))
  99. #options
  100. #if(ofiles) {
  101. # save(PCstd, ascii=TRUE, file="$plotsdir/PCs_of_data.dat")
  102. # save(fvar, ascii=TRUE, file="$plotsdir/FVARs_of_data.dat")
  103. #}
  104. invisible(list(PCs=PCstd,FVARs=fvar,EOFs=EOF,CORRs=CORR,Rtest=R))
  105. }
  106. CFU_MCA<-function(datax, datay, cl = 0.98, ofiles = FALSE) {
  107. #
  108. # Computes a Maximum Covariance Analysis (MCA) between datax and datay.
  109. #
  110. # Description:
  111. #
  112. # Returns a vector of squared covariance fraction (SCFs) explained by each pair of
  113. # covariability modes, a vector of correlation coefficient (RUVs) between expansion
  114. # coefficients (ECs) that measures their linear relationship, and a set of regression (MCAs)
  115. # and correlation (CORRs) maps associated with the covariability modes. Note that both,
  116. # MCAs and CORRs, are 'homogeneous' patterns obtained as regression/correlation between
  117. # each field (predictor, predictand) and its expansion coefficient.
  118. # The threshold for the correlation coefficient, based on a two-tailed Student's t-test,
  119. # is also produced.
  120. # If applied, also returns ascii files for SCFs, RUVs, and ECs.
  121. #
  122. # Usage:
  123. #
  124. # CFU_MCA(datax, datay, sl = 0.98, ofiles = FALSE)
  125. #
  126. # Arguments:
  127. #
  128. # datax: Array containing the anomalies field for the predictor
  129. #
  130. # datay: Array containing the anomalies field for the predictand
  131. #
  132. # cl: Value of the confidence level for the correlation map.
  133. # It is based on a two-tailed Student's t-test.
  134. # Default is cl=0.98 (98% confidence level; 2% significance level).
  135. #
  136. # ofiles: Logical.
  137. #
  138. # Output:
  139. #
  140. # $SCFs: Vector of squared covariance fractions (nm)
  141. # $RUVs: Vector of correlations between expansion coefficients (nm)
  142. # $ECs_U: Array of expansion coefficients of predictor field (nt, nm)
  143. # $MCAs_U: Array of covariability patterns of predictor field (c(dim), nm)
  144. # $CORRs_U:Array of corr. maps associated with predictor field (c(dim), nm)
  145. # $ECs_V: Array of expansion coefficients of predictand field (nt, nm)
  146. # $MCAs_V: Array of covariability patterns of predictand field (c(dim), nm)
  147. # $CORRs_V:Array of corr. maps associated with predictand field (c(dim), nm)
  148. # $Rtest: Value of the statitically significant corr. coefficient at cl
  149. #
  150. # Author:
  151. #
  152. # Javier Garcia-Serrano <jgarcia@ic3.cat> 24 September 2010
  153. #
  154. dx=dim(datax)
  155. if (length(dx) == 3){
  156. nlonx=dx[1]; nlatx=dx[2]; nsx=nlonx*nlatx; nt=dx[3]
  157. } else {
  158. nlonx=1; nlatx=dx[1]; nsx=nlonx*nlatx; nt=dx[2]
  159. }
  160. X=array(dim=c(nsx,nt))
  161. for (i in 1:nlatx){
  162. X[(nlonx*(i-1)+1):(nlonx*(i-1)+nlonx),]=datax[,i,]
  163. }
  164. not=is.na(X[,1])
  165. yesx=array(dim=c(nsx))
  166. counterx=0
  167. for (i in 1:nsx){
  168. if (not[i]=='FALSE'){counterx=counterx+1; yesx[counterx]=i}
  169. }
  170. yesx=yesx[1:counterx]
  171. Xyes=X[yesx,] #(nsx_with_data,nt)
  172. dy=dim(datay)
  173. if (length(dy) == 3){
  174. nlony=dy[1]; nlaty=dy[2]; nsy=nlony*nlaty; nt=dy[3]
  175. } else {
  176. nlony=1; nlaty=dy[1]; nsy=nlony*nlaty; nt=dy[2]
  177. }
  178. Y=array(dim=c(nsy,nt))
  179. for (i in 1:nlaty){
  180. Y[(nlony*(i-1)+1):(nlony*(i-1)+nlony),]=datay[,i,]
  181. }
  182. not=is.na(Y[,1])
  183. yesy=array(dim=c(nsy))
  184. countery=0
  185. for (i in 1:nsy){
  186. if (not[i]=='FALSE'){countery=countery+1; yesy[countery]=i}
  187. }
  188. yesy=yesy[1:countery]
  189. Yyes=Y[yesy,] #(nsy_with_data,nt)
  190. #covariance matrix
  191. covXY=(Xyes%*%t(Yyes))/(nt-1)
  192. #MCA analysis
  193. MCA=svd(covXY)
  194. #outputs
  195. nm=10 #number of modes to be retained
  196. SV=MCA$d #singular values
  197. SV2=SV*SV; S=sum(SV2)
  198. SCF=SV2/S #squared covariance fraction
  199. SCF=SCF[1:nm]*100
  200. Ux=MCA$u
  201. Vy=MCA$v
  202. Un=Ux[,1:nm]; Vn=Vy[,1:nm]
  203. ECu=t(Xyes)%*%Un; ECv=t(Yyes)%*%Vn #(nt,nm)
  204. ECu_std=array(dim=c(nt,nm))
  205. ECv_std=array(dim=c(nt,nm))
  206. for (i in 1:nm){
  207. Su=sd(ECu[,i]); ECu_std[,i]=ECu[,i]/Su
  208. Sv=sd(ECv[,i]); ECv_std[,i]=ECv[,i]/Sv
  209. }
  210. RUV=array(dim=c(1,nm))
  211. for (i in 1:nm){
  212. RUV[i]=cor(ECu_std[,i],ECv_std[,i])
  213. }
  214. Uyes=(Xyes%*%ECu_std)/nt #(ns_with_data,nm)
  215. Vyes=(Yyes%*%ECv_std)/nt
  216. mcaU=array(dim=c(nsx,nm)); mcaU[yesx,]=Uyes
  217. mcaV=array(dim=c(nsy,nm)); mcaV[yesy,]=Vyes
  218. MCAU=array(dim=c(nlonx,nlatx,nm))
  219. MCAV=array(dim=c(nlony,nlaty,nm))
  220. for (i in 1:nlatx){
  221. MCAU[,i,]=mcaU[(nlonx*(i-1)+1):(nlonx*(i-1)+nlonx),]
  222. }
  223. for (i in 1:nlaty){
  224. MCAV[,i,]=mcaV[(nlony*(i-1)+1):(nlony*(i-1)+nlony),]
  225. }
  226. CORRUyes=array(dim=c(counterx,nm))
  227. CORRVyes=array(dim=c(countery,nm))
  228. for (i in 1:nm){
  229. for (j in 1:counterx){
  230. CORRUyes[j,i]=cor(Xyes[j,],ECu_std[,i])
  231. }
  232. for (j in 1:countery){
  233. CORRVyes[j,i]=cor(Yyes[j,],ECv_std[,i])
  234. }
  235. }
  236. corrU=array(dim=c(nsx,nm)); corrU[yesx,]=CORRUyes
  237. corrV=array(dim=c(nsy,nm)); corrV[yesy,]=CORRVyes
  238. CORRU=array(dim=c(nlonx,nlatx,nm))
  239. CORRV=array(dim=c(nlony,nlaty,nm))
  240. for (i in 1:nlatx){
  241. CORRU[,i,]=corrU[(nlonx*(i-1)+1):(nlonx*(i-1)+nlonx),]
  242. }
  243. for (i in 1:nlaty){
  244. CORRV[,i,]=corrV[(nlony*(i-1)+1):(nlony*(i-1)+nlony),]
  245. }
  246. #t-Test
  247. cl=cl; cl2=(1-cl)/2
  248. t=qt(cl+cl2, nt-2); R=sqrt((t*t)/((t*t)+nt-2))
  249. #options
  250. #if(ofiles) {
  251. # save(SCF, ascii=TRUE, file="$plotsdir/SCFs_of_data.dat")
  252. # save(RUV, ascii=TRUE, file="$plotsdir/RUVs_of_data.dat")
  253. # save(ECu_std, ascii=TRUE, file="$plotsdir/ECsU_of_data.dat")
  254. # save(ECv_std, ascii=TRUE, file="$plotsdir/ECsV_of_data.dat")
  255. #}
  256. invisible(list(SCFs=SCF,RUVs=RUV,ECs_U=ECu_std,MCAs_U=MCAU,CORRs_U=CORRU,ECs_V=ECv_std,MCAs_V=MCAV,CORRs_V=CORRV,Rtest=R))
  257. }
  258. CFU_lonlat2ns<-function(data, miss.val = FALSE){
  259. #
  260. # Reshape a 3D array into a 2D array handling only spatial dimensions
  261. #
  262. # Description:
  263. #
  264. # Returns a 2D array of 3D data placing the spatial dimensions into a vector according
  265. # to GrADS protocol, and keeps the temporal dimension with no change.
  266. # It localizes missing values and returns an (spatial) index with no-missing values positions.
  267. #
  268. # Usage: CFU_latlon2ns(data, miss.val = FALSE)
  269. #
  270. # Input:
  271. #
  272. # data: 3-dimensional array, e.g. (longitude, latitude, time)
  273. #
  274. # Output:
  275. #
  276. # matrix: 2-dimensional array with 'ns' spatial dimensions, e.g. (ns, time)
  277. #
  278. # Authors:
  279. #
  280. # Javier Garcia-Serrano <jgarcia@ic3.cat> 30 September 2010
  281. d=dim(data)
  282. nlon=d[1]; nlat=d[2]; ns=nlon*nlat; nt=d[3]
  283. M=array(dim=c(ns,nt))
  284. for (i in 1:nlat){
  285. M[(nlon*(i-1)+1):(nlon*(i-1)+nlon),]=data[,i,]
  286. }
  287. not=is.na(M[,1])
  288. yes=array(dim=c(ns))
  289. counter=0
  290. for (i in 1:ns){
  291. if (not[i]=='FALSE'){counter=counter+1; yes[counter]=i}
  292. }
  293. yes=yes[1:counter]
  294. Myes=M[yes,] #(ns_with_data,nt)
  295. #printed in screen
  296. print(ns)
  297. print(counter)
  298. #outputs
  299. if(miss.val) { Myes <- M }
  300. invisible(list(matrix=Myes,nt=nt,ns_original=ns,ns_noNaN=counter,index_noNaN=yes))
  301. }
  302. CFU_ns2lonlat<-function(data, nlon=1, nlat=1, yes=NULL){
  303. #
  304. # Reshape a 2D array into a 3D array handling only spatial dimensions
  305. #
  306. # Description:
  307. #
  308. # Returns a 2D array of 3D data placing the spatial dimensions into a vector according
  309. # to GrADS protocol, and keeps the temporal dimension with no change.
  310. # It localizes missing values and returns an (spatial) index with no-missing values positions.
  311. #
  312. # Usage: CFU_latlon2ns(data, miss.val = FALSE)
  313. #
  314. # Input:
  315. #
  316. # data: 3-dimensional array, e.g. (longitude, latitude, time)
  317. #
  318. # Output:
  319. #
  320. # matrix: 2-dimensional array with 'ns' spatial dimensions, e.g. (ns, time)
  321. #
  322. # Authors:
  323. #
  324. # Javier Garcia-Serrano <jgarcia@ic3.cat> 06 October 2010
  325. nlon=max(abs(nlon), 1); nlat=max(abs(nlat), 1); ns=nlon*nlat
  326. d=dim(data)
  327. nt=d[2]
  328. miss.val=is.null(yes)
  329. if(miss.val=='TRUE'){
  330. M=array(dim=c(nlon,nlat,nt))
  331. for (i in 1:nlat){
  332. M[,i,]=data[(nlon*(i-1)+1):(nlon*(i-1)+nlon),]
  333. }
  334. } else {
  335. Myes=array(dim=c(ns,nt)); Myes[yes,]=data
  336. M=array(dim=c(nlon,nlat,nt))
  337. for (i in 1:nlat){
  338. M[,i,]=Myes[(nlon*(i-1)+1):(nlon*(i-1)+nlon),]
  339. }
  340. }
  341. matrix=M
  342. matrix
  343. }
  344. CFU_detrend<-function(vector){
  345. #
  346. # Remove the linear trend in a time-series
  347. #
  348. # Description:
  349. #
  350. # Returns a vector with the same length as the original (nt) but in which
  351. # the linear trend has been removed.
  352. # No change is produced if vector is a vector of missing values.
  353. #
  354. # Usage: CFU_detrend(vector)
  355. #
  356. # Input:
  357. #
  358. # vector: 1-dimensional array, time (nt dimension)
  359. #
  360. # Output:
  361. #
  362. # 1-dimensional array without the linear trend (nt)
  363. #
  364. # Authors:
  365. #
  366. # Javier Garcia-Serrano <jgarcia@ic3.cat> 05 October 2010
  367. # Modified by jgarcia & vguemas@ic3.cat (27 April 2011) : to convert output into a vector (before, a list)
  368. nt=length(vector)
  369. if (is.na(vector[1])=='TRUE'){
  370. print("vector contains missing values")
  371. detrend=array(dim=nt)
  372. } else {
  373. detrend=resid(lm(vector ~ seq(vector)))
  374. }
  375. detrend=as.vector(detrend)
  376. #outputs
  377. detrend
  378. }
  379. CFU_testRR<-function(R1, N1, R2, N2, cl = 0.95){
  380. #
  381. # Testing the equality of correlation coefficients from independent samples
  382. #
  383. # Description:
  384. #
  385. # Statistical test for the equality of correlation coefficients (R1, R2)
  386. # from two independent samples. These samples should belong to two populations,
  387. # e.g. dynamical prediction (GCM) and statistical prediction; and the hypothesis
  388. # will test the equality of the population correlation (bilateral test).
  389. #
  390. # H0: R1==R2 vs. H1: R1=/=R2
  391. #
  392. # The statistic Z follows a N(0,1) distribution; if |Z| > Z_alfa/2, H0 is rejected.
  393. #
  394. # Usage: CFU_testRR(R1, N1, R2, N2, cl)
  395. #
  396. # Input:
  397. #
  398. # R1: number, correlation of samples #1
  399. #
  400. # N1: number, nº of elements in the sample (length #1)
  401. #
  402. # R2: number, correlation of samples #2
  403. #
  404. # N2: number, nº of elements in the sample (length #2)
  405. #
  406. # cl: Value of the confidence level.
  407. # Default is cl=0.95 (95% confidence level; 5% significance level).
  408. #
  409. # Output:
  410. #
  411. # value: binary output (0,1) / 0 = H0 accepted, 1 = H0 rejected
  412. #
  413. # p_value: area under the N(0,1) density curve to the right of endpoint |Z|
  414. #
  415. # Authors:
  416. #
  417. # Javier Garcia-Serrano <jgarcia@ic3.cat> 02 December 2010
  418. # Modified by jgarcia & lrodrigues@ic3.cat (13 December 2011) : Correcting p-values
  419. # in two-tailed test (alfa is now multiplied by 2).
  420. Y1=0.5*log((1+R1)/(1-R1)); Y2=0.5*log((1+R2)/(1-R2))
  421. B=(1/(N1-3)) + (1/(N2-3)); Z=abs(Y1-Y2)/sqrt(B) #abs() -> two-tailed test
  422. cl=cl; cl2=(1-cl)/2; Z_cl=qnorm(cl+cl2)
  423. if (is.na(Z)==FALSE){
  424. if (Z > Z_cl){ value=1 } else { value=0 }
  425. } else {
  426. value=NA
  427. }
  428. #
  429. #p=pnorm(Z)-pnorm(-Z) #two-tailed / bilateral --> confidence level
  430. #p=1-p # --> significance level
  431. #p=p/2 # alfa/2 --> p_value
  432. #
  433. if (is.na(Z)==FALSE){
  434. p=2*pnorm(Z,lower.tail=FALSE)
  435. } else {
  436. p=NA
  437. }
  438. #outputs
  439. invisible(list(value=value, p_value=p))
  440. }
  441. CFU_testR<-function(R, N, cl = 0.95, test="two"){
  442. #
  443. # Testing the Null-Hypothesis of population correlation equal to zero
  444. #
  445. # Description:
  446. #
  447. # Statistical test for sample correlation coefficient (R) according to
  448. # the Null Hypothesis of population correlation coefficient (RO) equal to zero.
  449. #
  450. # H0: RO==0 vs. H1: RO=/=0
  451. #
  452. # The statistic t follows a Student's (T-test) distribution:
  453. #
  454. # if t > t_alfa,N-2 (one-tailed) H0 is rejected
  455. #
  456. # if |t| > t_alfa/2,N-2 (two-tailed) H0 is rejected
  457. #
  458. # Usage: CFU_testR(R, N, cl, test=c("one","two"))
  459. #
  460. # Input:
  461. #
  462. # R: number, sample correlation
  463. #
  464. # N: number, nº of elements in the sample
  465. #
  466. # cl: Value of the confidence level.
  467. # Default is cl=0.95 (95% confidence level; 5% significance level).
  468. #
  469. # test: string of characters that can be "one" or "two" (by default) according
  470. # to required test; one-tailed, unilateral (the former) o two-tailed,
  471. # bilateral (the latter).
  472. #
  473. # Output:
  474. #
  475. # value: binary output (0,1) / 0 = H0 accepted, 1 = H0 rejected
  476. #
  477. # p_value: area under the Student's T density curve to the right of endpoint |t|
  478. #
  479. # Authors:
  480. #
  481. # Javier Garcia-Serrano <jgarcia@ic3.cat> 08 December 2010
  482. #
  483. # Modified by jgarcia & lrodrigues@ic3.cat (13 July 2011) : Correcting p-values
  484. # in two-tailed test (alfa is now multiplied by 2).
  485. t=R*sqrt((N-2)/(1-R**2))
  486. cl=cl
  487. if (test=="one"){
  488. t_cl=qt(cl, N-2)
  489. if (abs(t) > t_cl){ value=1 } else { value=0 }
  490. #
  491. if (t > 0){
  492. p=pt(t, N-2, lower.tail=FALSE)
  493. } else { p=pt(t, N-2, lower.tail=TRUE) }
  494. }
  495. if (test=="two"){
  496. cl2=(1-cl)/2; t_cl=qt(cl+cl2, N-2)
  497. if (abs(t) > t_cl){ value=1 } else { value=0 }
  498. #
  499. if (t > 0){
  500. p=pt(t, N-2, lower.tail=FALSE)*2
  501. } else { p=pt(t, N-2, lower.tail=TRUE)*2 }
  502. }
  503. #ouputs
  504. invisible(list(value=value, p_value=p))
  505. }
  506. CFU_AA<-function(data, lat, yes=NULL){
  507. #
  508. # Compute the area-average of a 2D (longitude x latitude) array
  509. #
  510. # Description:
  511. #
  512. # Area-average (AA) of "data" over the spatial domain described by 'longitude' and
  513. # 'latitude'.
  514. # The weights for the field-mean (AA) are based on the cosine of the latitude at each
  515. # gridpoint, i.e. all data in a latitudinal circle (all longitudes) have the same
  516. # weight, cosine("lat").
  517. #
  518. # Usage: CFU_AA(data, lat, yes)
  519. #
  520. # Input:
  521. #
  522. # data: 2-dimensional array (longitude, latitude; in this order)
  523. #
  524. # lat: vector containing the latitude coordinates
  525. #
  526. # yes: vector containing the position of non-NaN values, so 'good' values
  527. # (in a ns=nlon*nlat, GrADS format)
  528. #
  529. # Output:
  530. #
  531. # data_ave; value of the area-average
  532. #
  533. # Authors:
  534. #
  535. # Javier Garcia-Serrano <jgarcia@ic3.cat> 03 December 2010
  536. # Modified by Virginie Guemas <vguemas@ic3.cat> (March 2011) : Weight = 0 for NA grid point
  537. ycos=cos((lat*pi)/180); nlat=dim(data)[2]; nlon=dim(data)[1]; ns=nlon*nlat
  538. weight=array(dim=c(nlon,nlat))
  539. for (i in 1:nlon){
  540. weight[i,]=ycos
  541. }
  542. #weight=weight/sum(weight) #normalization
  543. data=data*weight #weightening
  544. miss.val=is.null(yes)
  545. if(miss.val=='TRUE'){
  546. M=array(dim=c(ns))
  547. weight2=array(dim=c(ns))
  548. for (i in 1:nlat){
  549. M[(nlon*(i-1)+1):(nlon*(i-1)+nlon)]=data[,i]
  550. weight2[(nlon*(i-1)+1):(nlon*(i-1)+nlon)]=weight[,i]
  551. }
  552. not=is.na(M)
  553. yes=array(dim=c(ns))
  554. counter=0
  555. for (i in 1:ns){
  556. if (not[i]=='FALSE'){counter=counter+1; yes[counter]=i}
  557. }
  558. yes=yes[1:counter]
  559. tmp=sum(M[yes])
  560. sweight=sum(weight2[yes])
  561. data_ave=tmp/sweight
  562. #data_ave=sum(M[yes])
  563. } else {
  564. yes=yes
  565. M=array(dim=c(ns))
  566. weight2=array(dim=c(ns))
  567. for (i in 1:nlat){
  568. M[(nlon*(i-1)+1):(nlon*(i-1)+nlon)]=data[,i]
  569. weight2[(nlon*(i-1)+1):(nlon*(i-1)+nlon)]=weight[,i]
  570. }
  571. tmp=sum(M[yes])
  572. sweight=sum(weight2[yes])
  573. data_ave=tmp/sweight
  574. #data_ave=sum(M[yes])
  575. }
  576. #outputs
  577. data_ave
  578. }
  579. CFU_neff<-function(vector, fig=FALSE){
  580. #
  581. # Compute the effective degrees of freedom of a time series
  582. #
  583. # Description:
  584. #
  585. # Number of effective degrees of freedom of "vector".
  586. # Based on: Zieba, A. (2010): Effective number of observations and unbiased estimators
  587. # of variance for autocorrelated data - an overview. Metrol. Meas. Syst. Vol XVII, No. 1,
  588. # pp. 3-16; index 330930, ISSN 0860-8229. www.metrology.pg.gda.pl
  589. #
  590. # Usage: CFU_neff(vector)
  591. #
  592. # Input:
  593. #
  594. # vector: 1-dimensional array, time (nt dimension)
  595. #
  596. # fig: logical, if plot in the autocorrelation of "vector" is desired
  597. #
  598. # Output:
  599. #
  600. # neff; number of effective degrees of freedom
  601. #
  602. # Authors:
  603. #
  604. # Created by Caio A. S. Coelho <caio.coelho@cptec.inpe.br>
  605. # Implemented in CFU_Rfunc by Javier García-Serrano <jgarcia@ic3.cat> (August 2011)
  606. dof=length(vector)
  607. a=acf(vector,lag.max=dof-1,plot=fig)$acf[2:dof,1,1]
  608. s=0
  609. for (k in 1:(dof-1)){
  610. s=s+(((dof-k)/dof)*(a[k]**2))
  611. }
  612. neff=(dof/(1+(2*s)))-1
  613. #if (neff>dof){neff=dof}
  614. #outputs
  615. #list(neff=neff)
  616. neff
  617. }
  618. CFU_BS <- function(obs,pred,thresholds = seq(0,1,0.1)) {
  619. #
  620. # Compute the BS and its decomposition given a set of probability predictions of binary event
  621. #
  622. # Description:
  623. #
  624. # Returns the values of the BS and its standard decomposition as well as the addition of the
  625. # two winthin-bin extra components (Stephenson et al., 2008). It also solves the bias-corrected
  626. # decomposition of the BS (Ferro and Fricker, 2012). BSS having the climatology as the reference
  627. # forecast.
  628. #
  629. # Usage:
  630. #
  631. # CFU_BS(obs,pred,thresholds = seq(0,1,0.1))
  632. #
  633. # Input:
  634. #
  635. # obs: Vector of binary observations (1 or 0)
  636. #
  637. # pred: Vector of probablistic predictions [0,1]
  638. #
  639. # thresholds: Values used to bin the forecasts. By default the bins are
  640. # {[0,0.1), [0.1, 0.2), ... [0.9, 1]}.
  641. #
  642. # Output:
  643. #
  644. # $rel: standard reliability
  645. # $res: standard resolution
  646. # $unc: standard uncertainty
  647. # $bs: Brier score
  648. # $bs_check_res: rel-res+unc
  649. # $bss_res: res-rel/unc
  650. # $gres: generalized resolution
  651. # $bs_check_gres: rel-gres+unc
  652. # $bss_gres: gres-rel/unc
  653. # $rel_bias_corrected: bias-corrected rel
  654. # $gres_bias_corrected: bias-corrected gres
  655. # $unc_bias_corrected: bias-corrected unc
  656. # $bss_bias_corrected: gres_bias_corrected-rel_bias_corrected/unc_bias_corrected
  657. # $nk: number of forecast in each bin
  658. # $fkbar: average probability of each bin
  659. # $okbar: relative frequency that the observed event occurred
  660. # $bins: bins used
  661. # $pred: values with which the forecasts are verified
  662. # $obs: probability forecasts of the event
  663. #
  664. # References:
  665. #
  666. # Wilks (2006) Statistical Methods in the Atmospheric Sciences.
  667. # Stephenson et al. (2008). Two extra components in the Brier score decomposition. Weather
  668. # and Forecasting, 23: 752-757.
  669. # Ferro and Fricker (2012). A bias-corrected decomposition of the BS. Quarterly Journal of
  670. # the Royal Meteorological Society, DOI: 10.1002/qj.1924.
  671. #
  672. # Example:
  673. #
  674. # a=runif(10)
  675. # b=round(a)
  676. # x=CFU_BS(b,a)
  677. # x$bs-x$bs_check_res
  678. # x$bs-x$bs_check_gres
  679. # x$rel_bias_corrected-x$gres_bias_corrected+x$unc_bias_corrected
  680. #
  681. # Author:
  682. #
  683. # Luis Rodrigues <lrodrigues@ic3.cat> 16 April 2012
  684. #
  685. if (max(pred) > 1 | min(pred) < 0) {
  686. cat("Predictions outside [0,1] range. \n Are you certain this is a probability forecast? \n")
  687. } else if (max(obs) != 1 & min(obs) != 0) {
  688. cat("Binary events must be either 0 or 1. \n Are you certain this is a binary event? \n")
  689. } else {
  690. nbins=length(thresholds)-1 # Number of bins
  691. n=length(pred)
  692. bins=as.list(paste("bin",1:nbins,sep=""))
  693. for (i in 1:nbins) {
  694. if (i == nbins) {
  695. bins[[i]]=list(which(pred>=thresholds[i] & pred<=thresholds[i+1]))
  696. } else {
  697. bins[[i]]=list(which(pred>=thresholds[i] & pred<thresholds[i+1]))
  698. }
  699. }
  700. #
  701. fkbar=okbar=nk=array(0,dim=nbins)
  702. for (i in 1:nbins) {
  703. nk[i]=length(bins[[i]][[1]])
  704. fkbar[i]=sum(pred[bins[[i]][[1]]])/nk[i]
  705. okbar[i]=sum(obs[bins[[i]][[1]]])/nk[i]
  706. }
  707. #
  708. obar=sum(obs)/length(obs)
  709. relsum=ressum=term1=term2=0
  710. for (i in 1:nbins) {
  711. if (nk[i] > 0) {
  712. relsum=relsum+nk[i]*(fkbar[i]-okbar[i])^2
  713. ressum=ressum+nk[i]*(okbar[i]-obar)^2
  714. for (j in 1:nk[i]) {
  715. term1=term1+(pred[bins[[i]][[1]][j]]-fkbar[i])^2
  716. term2=term2+(pred[bins[[i]][[1]][j]]-fkbar[i])*(obs[bins[[i]][[1]][j]]-okbar[i])
  717. }
  718. }
  719. }
  720. rel=relsum/n
  721. res=ressum/n
  722. unc=obar*(1-obar)
  723. bs=sum((pred-obs)^2)/n
  724. bs_check_res=rel-res+unc
  725. bss_res=(res-rel)/unc
  726. gres=res-term1*(1/n)+term2*(2/n) # Generalized resolution
  727. bs_check_gres=rel-gres+unc # BS using GRES
  728. bss_gres=(gres-rel)/unc # BSS using GRES
  729. #
  730. # Estimating the bias-corrected components of the BS
  731. #
  732. term3=array(0,nbins)
  733. for (i in 1:nbins) {
  734. term3[i]=(nk[i]/(nk[i]-1))*okbar[i]*(1-okbar[i])
  735. }
  736. term_a=sum(term3,na.rm=T)/n
  737. term_b=(obar*(1-obar))/(n-1)
  738. rel_bias_corrected=rel-term_a
  739. gres_bias_corrected=gres-term_a+term_b
  740. if (rel_bias_corrected < 0 || gres_bias_corrected < 0 ) {
  741. rel_bias_corrected2=max(rel_bias_corrected,rel_bias_corrected-gres_bias_corrected,0)
  742. gres_bias_corrected2=max(gres_bias_corrected,gres_bias_corrected-rel_bias_corrected,0)
  743. rel_bias_corrected=rel_bias_corrected2
  744. gres_bias_corrected=gres_bias_corrected2
  745. }
  746. unc_bias_corrected=unc+term_b
  747. bss_bias_corrected=(gres_bias_corrected-rel_bias_corrected)/unc_bias_corrected
  748. #
  749. if (round(bs,8)==round(bs_check_gres,8) & round(bs_check_gres,8)==round((rel_bias_corrected-gres_bias_corrected+unc_bias_corrected),8)) {
  750. # cat("No error found \n")
  751. # cat("BS = REL-GRES+UNC = REL_lessbias-GRES_lessbias+UNC_lessbias \n")
  752. }
  753. #
  754. invisible(list(rel=rel,res=res,unc=unc,bs=bs,bs_check_res=bs_check_res,bss_res=bss_res,gres=gres,bs_check_gres=bs_check_gres,bss_gres=bss_gres,rel_bias_corrected=rel_bias_corrected,gres_bias_corrected=gres_bias_corrected,unc_bias_corrected=unc_bias_corrected,bss_bias_corrected=bss_bias_corrected,nk=nk,fkbar=fkbar,okbar=okbar,bins=bins,pred=pred,obs=obs))
  755. #
  756. } # end of if
  757. #
  758. }
  759. CFU_plotmapmostliktercile <- function(lon,lat,ptc,
  760. tit='Probability forecast of most likely tercile (%)',file='G_PFC_map.ps') {
  761. #
  762. # Produce probabilistic forecast map of most likely tercile of a forecast.
  763. #
  764. # Description:
  765. #
  766. # Generates vector graphics file of a probabilistic forecast map showing the most likely tercile.
  767. # Based on an adapted version of Caio Coelho's EURO-BRISA function.
  768. #
  769. # Usage:
  770. #
  771. # CFU_plotmapmostliktercile(lon,lat,ptc,tit='Probability forecast of most likely tercile (%)',file='G_PFC_map.ps')
  772. #
  773. # Arguments:
  774. #
  775. # lon: A vector of longitudes.
  776. #
  777. # lat: A vector of latitudes.
  778. #
  779. # ptc: Array of forecast terciles information for each ensemble member, output of CFU_PB().
  780. #
  781. # tit: Title of the Figure.
  782. #
  783. # file: Name of the postscript output file.
  784. #
  785. # Output:
  786. #
  787. # Vector graphics file of a probabilistic forecast map showing the most likely tercile.
  788. #
  789. # Author:
  790. #
  791. # Fabian Lienert <flienert@ic3.cat> 29 Oct 2012.
  792. #
  793. source('/cfu/pub/scripts/R/rclim.txt')
  794. nlon <- length(lon); nlat <- length(lat)
  795. nfc <- dim(ptc)[1]
  796. nlonr <- nlon-ceiling(length(lon[lon<180 & lon > 0]))
  797. # Probability of most likely tercile.
  798. PMLT <- (ptc[1,1,,]*NA)
  799. for (i in 1:nlon) {
  800. for (j in 1:nlat) {
  801. lo <- sum(ptc[,1,j,i],na.rm=T)/nfc
  802. mid <- sum(ptc[,2,j,i],na.rm=T)/nfc
  803. up <- sum(ptc[,3,j,i],na.rm=T)/nfc
  804. if (all(up > c(mid,lo))) {
  805. PMLT[j,i] <- up
  806. } else if (all(mid > c(up,lo))) {
  807. PMLT[j,i] <- 0
  808. } else if (all(lo > c(mid,up))) {
  809. PMLT[j,i] <- -lo
  810. }
  811. }
  812. }
  813. # EURO-BRISA figure. Modified Caio Coelho's function.
  814. plotmapmostliktercile <- function(lon,lat,data,maintit='',legtit='',equi=TRUE,bw=FALSE,
  815. cont=FALSE,reg=FALSE,...,lonlim=c(min(floor(lon)),max(ceiling(lon))),latlim=c(min(floor(lat)),max(ceiling(lat))),orientation=NULL,
  816. mapdat="world",xmaplim=c(min(floor(lon)),max(ceiling(lon))),ymaplim=c(min(floor(lat)),max(ceiling(lat))),
  817. longrds=NULL,latgrds=NULL,
  818. breaks=NULL,n=11,colours=NULL,
  819. roundleg=1,invert=FALSE) {
  820. rg<-range(data, na.rm=T)
  821. lowerlim <- rg[1]; upperlim <- rg[2]
  822. maximum <- max(abs(c(lowerlim,upperlim)))
  823. if(is.null(breaks)){
  824. if(lowerlim<0) breaks <- seq(-maximum,maximum,(maximum-(-maximum))/n)
  825. else breaks <- seq(lowerlim,upperlim,(upperlim-(lowerlim))/n)
  826. }
  827. if(is.null(colours)){
  828. if(!bw){
  829. # Check if range includes negative values to use appropriate colour scale.
  830. if (rg[1] <0) {
  831. if (!invert) {
  832. colours <- bluered(seq(0,n-1,by=1), "linear", yellow =TRUE)
  833. } else {
  834. colours <- rev(bluered(seq(0,n-1,by=1), "linear", yellow =TRUE))
  835. }
  836. } else {
  837. colours <- bluered(seq(0,n-1,by=1), "linear", white=0, invert=T)
  838. }
  839. } else {
  840. if (rg[1] <0) {
  841. colours <- grey(seq(0, 1, length = length(breaks)-1))
  842. colours <- c(colours[1:((n-1)/2)],rev(colours[(((n-1)/2)+1):n]))
  843. } else {
  844. colours <- grey(seq(0, 1, length = length(breaks)-1))
  845. }
  846. }
  847. }
  848. if (equi) {
  849. layout(matrix(1:2,ncol=1,nrow=2),heights=c(5,1))
  850. par(mar=c(6,2,6.5,1),cex.axis=1.0)
  851. if (reg) {
  852. layout(matrix(1:2,ncol=1,nrow=2),heights=c(5,1))
  853. par(mar=c(2,3,4,1),las=1,cex=1.4)
  854. }
  855. # Create probabilistic map.
  856. image(lon,lat,data,axes=F,col=colours,xlab='',ylab='',breaks=breaks)
  857. par(cex=2.1)
  858. if (cont) {contour(lon,lat,data,levels=round(breaks,1),labels=round(breaks,1),add=T,...)}
  859. # Check if lon is from 0 to 360 or -180 to 180 to use appropriate world map.
  860. if (min(lon)<0) {
  861. map('world',interior = F,add = T, lwd=2) # Low resolution world map (lon -180 to 180).
  862. } else {
  863. map('world2',interior = F,add = T, lwd=2) # Low resolution world map (lon 0 to 360).
  864. }
  865. box()
  866. map.axes()
  867. par(font.main=1,cex=1.4)
  868. title(maintit)
  869. # Adding colorbar.
  870. par(mar=c(4.5,2,0,1),mgp=c(1.5,0.3,0),las=1)
  871. if (reg) {
  872. par(mar=c(3,0.3,1.5,0.3),mgp=c(1.5,0.3,0),las=1)
  873. }
  874. image(c(1:n),1,t(t(c(1:n))),axes=F,col=colours,xlab='',ylab='')
  875. box()
  876. par(font.main=1,cex=1.3)
  877. if (maximum > 1) {
  878. axis(1,at=seq(1,9),tick=F,pos=0.8,cex.axis=1.5,
  879. labels = c("85-100","70-85","55-70","40-55"," ","40-55","55-70","70-85","85-100"))
  880. axis(3,at=5,tick=F,cex.axis=1.5,pos=-1.3,labels=c("White=central tercile most likely"))
  881. axis(3,at=2.5,tick=F,cex.axis=1.5,pos=1.3,labels=c("Below normal"))
  882. axis(3,at=7.5,tick=F,cex.axis=1.5,pos=1.3,labels=c("Above normal"))
  883. } else {
  884. axis(1,at=seq(0.5, length(breaks)-0.5),tick=F,
  885. labels=round(breaks[1:(length(breaks))],roundleg))
  886. }
  887. # Redefine font size.
  888. par(cex=1.4)
  889. # Add the title.
  890. title(xlab = legtit,cex.lab=1.4)
  891. }
  892. }
  893. postscript(paste(file,sep=''),paper="special",width=12,height=9.25,horizontal=F)
  894. plotmapmostliktercile(c(lon[c((nlonr+1):nlon)]-360,lon[1:nlonr]),rev(lat),t(PMLT[nlat:1,c((nlonr+1):nlon,1:nlonr)]*100),
  895. reg=T,maintit=tit,invert=F,breaks=c(-100,-85,-70,-55,-40,40,55,70,85,100),n=9)
  896. dev.off()
  897. }
  898. CFU_PB <- function(lon,lat,ano,fcavt=2:4,fcyr,fcsys=1,qt=3,ncpu=4) {
  899. #
  900. # Compute probabilistic bins (qt) of a forecast (fcyr) relative to the forecast climatology of the residual
  901. # forecasts excluding the selected forecast year (fcyr).
  902. # The maximal values of the lowest/highest quantiles is +/-10^+06 due to limitation in the function hist().
  903. # By default (qt=3) CFU_PB() computes terciles that can be plotted with CFU_plotmapmostliktercile().
  904. #
  905. # Description:
  906. #
  907. # Returns probabilistic bins of a forecast (fcyr) relative to the forecast climatology without selected forecast.
  908. #
  909. # Usage:
  910. #
  911. # CFU_PB(lon,lat,ano,fcavt=2:4,fcyr,fcsys=1,qt=3,ncpu=4)
  912. #
  913. # Arguments:
  914. #
  915. # lon: A vector of longitudes.
  916. #
  917. # lat: A vector of latitudes.
  918. #
  919. # ano: Array of forecast anomalies in CFU_load format.
  920. #
  921. # fcavt: A vector of time steps to average across.
  922. #
  923. # fcyr: Number of the forecast start year of the forecast.
  924. #
  925. # fcsys: Number of the forecast system.
  926. #
  927. # qt: Number of probabilistic bins 3: terciles, 4: quartiles, etc.
  928. #
  929. # ncpu: Number of CPUs to be used.
  930. #
  931. # Output:
  932. #
  933. # Probabilistic forecast information (1 or 0) at each grid point with
  934. # dimensions (number of ensemble members, qt, number of longitudes, latitudes).
  935. # Author:
  936. #
  937. # Fabian Lienert <flienert@ic3.cat> 29 Oct 2012.
  938. #
  939. library(doMC)
  940. registerDoMC(ncpu)
  941. # Probabilistic forecast.
  942. nlon <- length(lon); nlat <- length(lat)
  943. nyr <- dim(ano)[3]; nfc <- dim(ano)[2]; lt <- dim(ano)[4]
  944. PTC <- array(NA, dim=c(nfc,3,nlat,nlon))
  945. for (im in 1:nfc) {
  946. XY.p <- foreach(i=1:nlon) %dopar% {
  947. PL <- array(NA, dim=c(qt,nlat))
  948. for (j in 1:nlat) {
  949. # Mean over forecast range.
  950. f.p <- apply(ano[fcsys,im,-fcyr,fcavt,j,i],c(1),mean)
  951. # PDF of model climatology without selected forecast.
  952. qum <- quantile(f.p,probs=c((1:(qt-1))/qt),prob,na.rm=F,names=F,type=8)
  953. # Bins of selected forecast.
  954. f.p <- mean(ano[fcsys,im,fcyr,fcavt,j,i])
  955. if (all(c(qum,f.p)==0)) {
  956. f0 <- rep(0,qt)
  957. if (!is.integer(qt/2)) { f0[median(1:qt)] <- 1 }
  958. PL[,j] <- f0
  959. } else {
  960. PL[,j] <- hist(f.p,breaks=c(-1e+06,qum,1e+06),plot=F)$counts
  961. }
  962. }
  963. PL
  964. }
  965. for (i in 1:nlon) { PTC[im,,,i] <- XY.p[[i]] }
  966. }
  967. PTC
  968. }
  969. CFU_MODE <- function(ano,eof,mode=1) {
  970. #
  971. # Description:
  972. #
  973. # Project anomalies onto modes to get temporal evolution of the EOF mode selected.
  974. # Returns principal components (PCs) by area-weighted projection onto EOF pattern (from CFU_EOF()).
  975. # Able to handle NAs.
  976. #
  977. # Arguments:
  978. #
  979. # ano: Array of observed or forecast anomalies from CFU_ano() with dimensions
  980. # (number of forecast systems, ensemble members, start years, forecast months, latitudes, longitudes).
  981. #
  982. # eof: EOF object from CFU_EOF()
  983. #
  984. # mode: mode number in EOF object onto which to project
  985. #
  986. # Output:
  987. #
  988. # PC.ver: Array of PCs in verification format
  989. # (number of forecast systems, ensemble members, start years, forecast months).
  990. # PC.ver.em: Array of ensemble-mean PCs in verification format
  991. # (number of forecast systems, start years, forecast months).
  992. #
  993. # Author:
  994. #
  995. # Fabian Lienert <flienert@ic3.cat> 19 Nov 2012.
  996. #
  997. # Lauriane Batte <lauriane.batte@ic3.cat> March 2014. Bug-fixes: 1-Extra weighting of the anomalies before projection.
  998. # 2-Reversion of the anomalies along latitudes.
  999. # 3-Extra-normalisation not necessary.
  1000. #
  1001. # Virginie Guemas <virginie.guemas@ic3.cat> 17 March 2014. Bug-fixes:1-Another extra-normalisation.
  1002. # 2-15 lines to compute the em reduced to 1.
  1003. # Lauriane Batte <lauriane.batte@ic3.cat> 18 March 2014. Normalization by std before returning PCs to be coherent with CFU_EOF
  1004. #
  1005. # Virginie Guemas <virginie.guemas@ic3.cat> 11 April 2014. 1 - Removal of lon, lat, ncpu and neofs argument unused
  1006. # 2 - Security checks ano and eof consistency
  1007. # 3 - Removal of the mask which is already contained in the EOFs
  1008. # 4 - Removal of the PC normalization since we have chosen in CFU_EOF
  1009. # to normalize the EOFs and multiply the PCs by the normalization
  1010. # factor and the eigenvalue so that the restitution of the original
  1011. # field is done simply by PC * EOFs
  1012. # 5 - The new convention in CFU_EOF is to divide by the weights so that the
  1013. # reconstruction of the original field rather than the weighted field
  1014. # is obtained by PC * EOFs. The EOFs need therefore to be multiplied
  1015. # back by the weights before projection so that EOF * t(EOF) = 1
  1016. # 6 - Since W *X = PC * EOF if EOF is multiplied back by the weights,
  1017. # PC = W * X * t(EOF) and X the input field to be projected (X)
  1018. # needs to be multiplied by W.
  1019. #
  1020. # library(s2dverification) # To simplify some lines. Virginie
  1021. # Getting input dimensions. Fabian
  1022. nlon<- dim(ano)[6]; nlat <- dim(ano)[5]
  1023. nyr <- dim(ano)[3]; lt <- dim(ano)[4]
  1024. nfc <- dim(ano)[2]; nmod <- dim(ano)[1]
  1025. # Security checks. Virginie
  1026. if ( dim(eof$EOFs)[2] != nlat ) {
  1027. stop("Inconsistent number of latitudes between eof and input field")
  1028. }
  1029. if ( dim(eof$EOFs)[3] != nlon ) {
  1030. stop("Inconsistent number of longitudes between eof and input field")
  1031. }
  1032. # Initialization of pc.ver. Fabian.
  1033. pc.ver <- array(NA,dim=c(nmod,nfc,nyr,lt))
  1034. # Weights are already in EOF and shouldn't be accounted for twice. Lauriane
  1035. #e.1 <- eof$EOFs[mode,,]; mask <- eof$mask; wght <- eof$wght
  1036. # Now the EOF outputs from CFU_EOF are masked . Virginie
  1037. #e.1 <- eof$EOFs[mode,,]; mask <- eof$mask; wght <- rep(1,nlon*nlat)
  1038. #dim(wght) <- c(nlon,nlat)
  1039. # Since we have chosen as a new convention in CFU_EOF to divide by the
  1040. # weights so that the restitution of the original field is only obtained
  1041. # by PCs*EOFs, I multiply here back by the weigths to ensure that the
  1042. # projection is correct. Virginie
  1043. e.1 <- eof$EOFs[mode,,]*eof$wght
  1044. # Since W *X = PC * EOF after such multiplication by the weights,
  1045. # the PC is obtained by W * X * t(EOF) so we multiply the field to project
  1046. # by the weights. Virginie
  1047. ano <- ano*InsertDim(InsertDim(InsertDim(InsertDim(eof$wght,1,nmod),2,nfc),3,nyr),4,lt)
  1048. # The 100 lines below can be simplified in a few lines as done below.
  1049. # Indeed, we can loop on nmod, nfc, nyr and lt even if the length of these dimentions is only 1.
  1050. # Furthermore, the PC of the ensemble mean anomaly equals the ensemble means of the PC of single
  1051. # members. [ 1/n sum(Xi) ] * t(EOF) = 1/n sum [ Xi * t(EOF) ]. Virginie
  1052. # # For one realization only. Fabian
  1053. # if (nfc==1) {
  1054. # # Case for only one member should not be separate from several members. We can loop over 1 member only. Virginie.
  1055. # pc.ver.em <- FALSE
  1056. # # Why is there no loop over the models ? Virginie.
  1057. # for (iy in 1:nyr) {
  1058. # for (il in 1:lt) {
  1059. # if (!all(is.na(ano[1,1,iy,il,,]))) {
  1060. # #t1 <- (t(ano[nmod,nfc,iy,il,nlat:1,])*e.1)
  1061. # # Latitudes should not be reversed to stay coherent with CFU_EOF. Lauriane
  1062. # t1 <- (ano[nmod,nfc,iy,il,,]*e.1)
  1063. # # The division by sum(mask) is an additional extra-normalisation. Virginie
  1064. # #nominator <- (sum(t1*wght*mask,na.rm=TRUE)/sum(wght*mask,na.rm=TRUE))
  1065. # # The EOFs are already masked now in CFU_EOF. Virginie
  1066. # #nominator <- sum(t1*mask,na.rm=TRUE)
  1067. # nominator <- sum(t1,na.rm=TRUE)
  1068. # # Normalization should not be necessary. Projection is a simple scalar product. Lauriane
  1069. # #denominator <- (sum(e.1^2*wght*mask,na.rm=TRUE)/sum(wght*mask,na.rm=TRUE))
  1070. # denominator <- 1.
  1071. # pc.ver[1,1,iy,il] <- nominator/denominator
  1072. # }
  1073. # }
  1074. # }
  1075. # # Normalization by standard deviation. Lauriane
  1076. # #for (il in 1:lt) {
  1077. # # pc.ver[1,1,,il] <- pc.ver[1,1,,il]/sd(pc.ver[1,1,,il])
  1078. # #}
  1079. # # I have removed this normalization in agreement with the conventions chosen for CFU_EOF.
  1080. # # The EOFs are normalized to 1 and the simple product EOF*PC should restitute the original field.
  1081. # # Virginie
  1082. # } else {
  1083. # pc.ver.em <- array(NA,dim=c(nmod,nyr,lt))
  1084. # # Virginie. This single line below gives the same result as l. 1162-1176
  1085. # ano.em <- apply(ano,c(1,3,4,5,6),mean,na.rm=TRUE)
  1086. # for (imo in 1:nmod) {
  1087. # # Ensemble mean anomalies.
  1088. # #if (lt==1 | nyr==1 ) {
  1089. # # if (lt==1 & !nyr==1 ) {
  1090. # # ano.em <- apply(ano[imo,,,,,],c(2,3,4),mean,na.rm=TRUE)
  1091. # # dim(ano.em) <- c(nyr,1,nlat,nlon)
  1092. # # } else if (!lt==1 & nyr==1 ) {
  1093. # # ano.em <- apply(ano[imo,,,,,],c(2,3,4),mean,na.rm=TRUE)
  1094. # # dim(ano.em) <- c(1,lt,nlat,nlon)
  1095. # # } else if (lt==1 & nyr==1 ) {
  1096. # # ano.em <- apply(ano[imo,,,,,],c(2,3),mean,na.rm=TRUE)
  1097. # # dim(ano.em) <- c(1,1,nlat,nlon)
  1098. # # }
  1099. # #} else {
  1100. # # ano.em <- apply(ano[imo,,,,,],c(2,3,4,5),mean,na.rm=TRUE)
  1101. # #}
  1102. # for (iy in 1:nyr) {
  1103. # for (il in 1:lt) {
  1104. # if (!all(is.na(ano.em[imo,iy,il,,]))) {
  1105. # #t1 <- (t(ano.em[imo,iy,il,nlat:1,])*e.1)
  1106. # # Latitudes should not be reversed to stay coherent with CFU_EOF. Lauriane.
  1107. # t1 <- (ano.em[imo,iy,il,,]*e.1)
  1108. # # The division by sum(mask) is an additional extra-normalisation. Virginie
  1109. # #nominator <- (sum(t1*wght*mask,na.rm=TRUE)/sum(wght*mask,na.rm=TRUE))
  1110. # # The EOFs are already masked now in CFU_EOF. Virginie
  1111. # #nominator <- sum(t1*mask,na.rm=TRUE)
  1112. # nominator <- sum(t1,na.rm=TRUE)
  1113. # # Normalization should not be necessary. Projection is a simple scalar product. Lauriane
  1114. # #denominator <- (sum(e.1^2*wght*mask,na.rm=TRUE)/sum(wght*mask,na.rm=TRUE))
  1115. # denominator <- 1.
  1116. # pc.ver.em[imo,iy,il] <- nominator/denominator
  1117. # }
  1118. # }
  1119. # }
  1120. # # Normalization by standard deviation: done for each model independently. Lauriane
  1121. # #for (il in 1:lt) {
  1122. # # pc.ver.em[imo,,il] <- pc.ver.em[imo,,il]/sd(pc.ver.em[imo,,il])
  1123. # #}
  1124. # # I have removed this normalization in agreement with the conventions chosen for CFU_EOF.
  1125. # # The EOFs are normalized to 1 and the simple product EOF*PC should restitute the original field.
  1126. # # Virginie
  1127. # # Ensemble member anomalies.
  1128. # for (im in 1:nfc) {
  1129. # for (iy in 1:nyr) {
  1130. # for (il in 1:lt) {
  1131. # if (!all(is.na(ano[imo,im,iy,il,,]))) {
  1132. # #t1 <- (t(ano[imo,im,iy,il,nlat:1,])*e.1)
  1133. # # Latitudes should not be reversed to stay coherent with CFU_EOF. Lauriane.
  1134. # t1 <- (ano[imo,im,iy,il,,]*e.1)
  1135. # # The division by sum(mask) is an additional extra-normalisation. Virginie
  1136. # #nominator <- (sum(t1*wght*mask,na.rm=TRUE)/sum(wght*mask,na.rm=TRUE))
  1137. # # The EOFs are already masked now in CFU_EOF. Virginie
  1138. # #nominator <- sum(t1*mask,na.rm=TRUE)
  1139. # nominator <- sum(t1,na.rm=TRUE)
  1140. # # Normalization should not be necessary. Projection is a simple scalar product. Lauriane
  1141. # #denominator <- (sum(e.1^2*wght*mask,na.rm=TRUE)/sum(wght*mask,na.rm=TRUE))
  1142. # denominator <- 1.
  1143. # pc.ver[imo,im,iy,il] <- nominator/denominator
  1144. # }
  1145. # }
  1146. # }
  1147. # # Normalization by standard deviation: done for each model and ensemble member independently. Lauriane
  1148. # #for (il in 1:lt) {
  1149. # # pc.ver[imo,im,,il] <- pc.ver[imo,im,,il]/sd(pc.ver[imo,im,,il])
  1150. # #}
  1151. # # I have removed this normalization in agreement with the conventions chosen for CFU_EOF.
  1152. # # The EOFs are normalized to 1 and the simple product EOF*PC should restitute the original field.
  1153. # # Virginie
  1154. # }
  1155. # }
  1156. # }
  1157. for (imo in 1:nmod) {
  1158. for (im in 1:nfc) {
  1159. for (iy in 1:nyr) {
  1160. for (il in 1:lt) {
  1161. if (!all(is.na(ano[imo,im,iy,il,,]))) {
  1162. pc.ver[imo,im,iy,il] <- sum(ano[imo,im,iy,il,,]*e.1,na.rm=TRUE)
  1163. }
  1164. }
  1165. }
  1166. }
  1167. }
  1168. # For compatibility with previous version, I keep the output of the ensemble mean although it is useless
  1169. # because the operation can be done easily outside of CFU_MODE if necessary. This should be removed in
  1170. # future. Virginie
  1171. pc.ver.em=Mean1Dim(pc.ver,2)
  1172. return(list(PC.ver=pc.ver,PC.ver.em=pc.ver.em))
  1173. }
  1174. CFU_DTF <- function(lon,lat,ano,gmt,ncpu=4) {
  1175. #
  1176. # Linearly regress out forecast global mean temperature trend (GMT) from forecast monthly anomalies
  1177. # of ten-year hindcasts started each year. The regression depends on the forecast lead month across all forecasts.
  1178. # NAs are excluded from the linear model fit, but kept in the output.
  1179. #
  1180. # Description:
  1181. #
  1182. # Returns linearly detrended forecast anomalies in the verification format (from CFU_ano()).
  1183. #
  1184. # Usage:
  1185. #
  1186. # CFU_DTF(lon,lat,ano,gmt)
  1187. #
  1188. # Arguments:
  1189. #
  1190. # lon: A vector of longitudes.
  1191. #
  1192. # lat: A vector of latitudes.
  1193. #
  1194. # ano: Array of forecast anomalies from CFU_ano() with dimensions
  1195. # (number of forecast systems, ensemble members, start years, forecast months, latitudes, longitudes).
  1196. #
  1197. # gmt: Array of forecast global mean temperature anomalies with dimensions
  1198. # (number of forecast systems, ensemble members, start years, forecast months).
  1199. #
  1200. # ncpu: Number of CPUs to be used.
  1201. #
  1202. # Output:
  1203. #
  1204. # ano.dt.ver: Array of detrended forecast anomalies from CFU_ano() with dimensions
  1205. # (number of forecast systems, ensemble members, start years, forecast months, latitudes, longitudes).
  1206. #
  1207. # Author:
  1208. #
  1209. # Fabian Lienert <flienert@ic3.cat> 16 Nov 2012.
  1210. #
  1211. library(doMC)
  1212. registerDoMC(ncpu)
  1213. nlon <- length(lon); nlat <- length(lat)
  1214. nyr <- dim(ano)[3]; lt <- dim(ano)[4]
  1215. nfc <- dim(ano)[2]; nmod <- dim(ano)[1]
  1216. ano.dt.ver <- array(NA,dim=c(nmod,nfc,nyr,lt,nlat,nlon))
  1217. ## Map of regression slopes.
  1218. r.XY <- array(NA,dim=c(nmod,lt,nlat,nlon))
  1219. for (imo in 1:nmod) {
  1220. XY.p <- foreach(i=1:nlon, .verbose=F) %dopar% {
  1221. PL1 <- array(NA,dim=c(nfc,nyr,lt,nlat))
  1222. PL2 <- array(NA,dim=c(lt,nlat))
  1223. for (j in 1:nlat) {
  1224. for (iy in 1:lt) {
  1225. # Forecast anomaly.
  1226. fctpr <- as.vector(ano[imo,,,iy,j,i])
  1227. # Forecast trend wrt. 1961-2011.
  1228. trend.f <- as.vector(gmt[imo,,,iy])
  1229. trend <- trend.f-mean(trend.f)
  1230. # Regress forecast anomaly onto trend and remove it from the forecast. Keep residual forecast anomaly.
  1231. if (!all(is.na(fctpr))) {
  1232. fit <- lm(fctpr~trend,na.action=na.exclude)
  1233. fctpr.dt <- residuals(fit)
  1234. dim(fctpr.dt) <- c(nfc,nyr)
  1235. PL1[,,iy,j] <- fctpr.dt
  1236. # Regression coefficient map.
  1237. PL2[iy,j] <- coefficients(fit)[[2]]
  1238. }
  1239. }
  1240. }
  1241. return(list(DFC=PL1,REG=PL2))
  1242. }
  1243. for (i in 1:nlon) {
  1244. ano.dt.ver[imo,,,,,i] <- XY.p[[i]]$DFC
  1245. r.XY[imo,,,i] <- XY.p[[i]]$REG
  1246. }
  1247. }
  1248. gc()
  1249. return(list(ano.dt.ver=ano.dt.ver,reg=r.XY))
  1250. }
  1251. CFU_DTO <- function(lon,lat,ano,yr1,yr2,yrint,gmt=F) {
  1252. #
  1253. # Linearly regress out global mean temperature trend (GMT) from observed monthly anomalies
  1254. # that coincide with hindcasts started on November 1st every 1,2,3, etc. years.
  1255. # The anomalies are selected from the first year that coincide with each hindcast.
  1256. # NAs are excluded from the linear model fit, but kept in the output.
  1257. #
  1258. # Description:
  1259. #
  1260. # Returns linearly detrended and original anomalies in a format to be used for CFU_EOF().
  1261. #
  1262. # Usage:
  1263. #
  1264. # CFU_DTO(lon,lat,ano,yr1,yr2,yrint,gmt=F)
  1265. #
  1266. # Arguments:
  1267. #
  1268. # lon: A vector of longitudes.
  1269. #
  1270. # lat: A vector of latitudes.
  1271. #
  1272. # ano: Array of anomalies from CFU_ano() with dimensions
  1273. # (1, 1, number of start years, forecast months, latitudes, longitudes).
  1274. #
  1275. # yr1: First start year.
  1276. #
  1277. # yr2: Last start year.
  1278. #
  1279. # yrint: Interval between hindcasts in years.
  1280. #
  1281. # gmt: Global mean temperature time series. (optional)
  1282. #
  1283. # Output:
  1284. #
  1285. # ano.dt.ver: An array of the linearly detrended anomalies in CFU verification format.
  1286. #
  1287. # ano.dt: An array of the linearly detrended anomalies with dimensions
  1288. # (number of months, latitudes, longitudes).
  1289. #
  1290. # ano: An array of the original input anomalies with dimensions
  1291. # (number of months, latitudes, longitudes).
  1292. #
  1293. # reg: An array containing a map of regression coefficients
  1294. # (number of latitudes, longitudes).
  1295. #
  1296. # Author:
  1297. #
  1298. # Fabian Lienert <flienert@ic3.cat> 19 Apr 2013.
  1299. #
  1300. nlon <- length(lon); nlat <- length(lat)
  1301. nyr <- dim(ano)[3]; lt <- dim(ano)[4]
  1302. print(nyr); print(lt)
  1303. if (all(gmt==F)) {
  1304. # Global mean temperature GMT from HadCRUT, climate explorer.
  1305. # http://climexp.knmi.nl/data/icrutem3_hadsst2_0-360E_-90-90N_n.dat
  1306. T1 <- read.table("/cfu/data/ukmo/hadcrut/icrutem3_hadsst2_0-360E_-90-90N_n.dat",row.names=1)
  1307. XT <- array(NA,dim=c(12,113))
  1308. for (im in 1:12) {
  1309. XT[im,] <- T1[[im]]
  1310. }
  1311. XT[XT==-9.999000e+02] <- NA
  1312. # GMT anomaly wrt 1356 mo, 113 yr.
  1313. tgl.obs <- ts(c(as.vector(XT),rep(NA,120)),deltat=1/12,start=c(1900,1),end=c(2022,12))
  1314. # GMT anomaly wrt verification period.
  1315. tgl.vp.nov.obs <- window(tgl.obs,start=c(yr1,11),end=c((yr2+lt/12),10))
  1316. tgl.vp.nov.obs <- tgl.vp.nov.obs*((yr2-yr1+lt/12)/113)
  1317. gmt <- tgl.vp.nov.obs
  1318. }
  1319. o.XY <- array(NA,dim=c(((yr2-yr1+lt/12)*12),nlat,nlon))
  1320. TP1 <- ano[1,1,,1:(yrint*12),nlat:1,]
  1321. TP1 <- aperm(TP1, c(2,1,3,4))
  1322. dim(TP1) <- c(nyr*yrint*12,nlat,nlon)
  1323. o.XY[1:(nyr*yrint*12),,] <- TP1
  1324. o.XY[((nyr*yrint*12)+1):((yr2-yr1+lt/12)*12),,] <- ano[1,1,nyr,((yrint*12)+1):lt,nlat:1,]
  1325. if (length(gmt)!=((yr2-yr1+lt/12)*12)) {
  1326. print(paste('GMT time series has not the same length than the array of anomalies.',length(gmt),((yr2-yr1+lt/12)*12)))
  1327. stop()
  1328. }
  1329. # Remove trend from the obs for the whole period.
  1330. o.XY.dt <- array(NA,dim=c(((yr2-yr1+lt/12)*12),nlat,nlon))
  1331. r.XY <- (o.XY.dt[1,,]*NA)
  1332. for (i in 1:nlon) {
  1333. for (j in 1:nlat) {
  1334. if (!is.na(o.XY[1,j,i])) {
  1335. # Regress anomaly onto GMT and remove it from the anomaly.
  1336. a1 <- o.XY[,j,i]
  1337. # Exclude NAs from linear model estimate, but keep them in the residual time series. (!=$residuals)
  1338. fit <- lm(a1~as.vector(gmt),na.action=na.exclude)
  1339. a1.dt <- residuals(fit)
  1340. o.XY.dt[,j,i] <- a1.dt
  1341. # Regression coefficient map.
  1342. r.XY[j,i] <- coefficients(fit)[[2]]
  1343. }
  1344. }
  1345. }
  1346. # Put detrended obs in verification format.
  1347. ano.dt.ver <- array(NA,dim=c(1,1,nyr,lt,nlat,nlon))
  1348. # Works for forecast start date in each year.
  1349. for (i in 1:nlon) {
  1350. for (j in 1:nlat) {
  1351. d.all <- ts(o.XY.dt[,j,i],deltat=1/12,start=c(yr1,11),end=c((yr2+lt/12),10))
  1352. if (!is.na(o.XY[1,j,i])) {
  1353. for (iy in 1:nyr) {
  1354. d.sl <- window(d.all,start=c((yr1+iy*yrint-yrint),11),end=c((yr1+lt/12+iy*yrint-yrint),10))
  1355. ano.dt.ver[1,1,iy,,((nlat+1)-j),i] <- d.sl
  1356. }
  1357. }
  1358. }
  1359. }
  1360. return(list(ano.dt=o.XY.dt,ano=o.XY,reg=r.XY,ano.dt.ver=ano.dt.ver,gmt=gmt,tgl.obs=tgl.obs))
  1361. }
  1362. CFU_DTO_an <- function(lon,lat,ano,yr1,yr2,yrint,gmt=F) {
  1363. #
  1364. # Linearly regress out global mean temperature trend (GMT) from observed annual anomalies
  1365. # that coincide with hindcasts started on November 1st every 1,2,3, etc. years.
  1366. # The anomalies are selected from the first year that coincide with each hindcast.
  1367. # NAs are excluded from the linear model fit, but kept in the output.
  1368. #
  1369. # Description:
  1370. #
  1371. # Returns linearly detrended and original anomalies in a format to be used for CFU_EOF().
  1372. #
  1373. # Usage:
  1374. #
  1375. # CFU_DTO_an(lon,lat,ano,yr1,yr2,yrint,gmt=F)
  1376. #
  1377. # Arguments:
  1378. #
  1379. # lon: A vector of longitudes.
  1380. #
  1381. # lat: A vector of latitudes.
  1382. #
  1383. # ano: Array of anomalies from CFU_ano() with dimensions
  1384. # (1, 1, number of start years, forecast years, latitudes, longitudes).
  1385. #
  1386. # yr1: First start year.
  1387. #
  1388. # yr2: Last start year.
  1389. #
  1390. # yrint: Interval between hindcasts in years.
  1391. #
  1392. # gmt: Global mean temperature time series. (optional)
  1393. #
  1394. # Output:
  1395. #
  1396. # ano.dt.ver: An array of the linearly detrended anomalies in CFU verification format.
  1397. #
  1398. # ano.dt: An array of the linearly detrended anomalies with dimensions
  1399. # (number of years, latitudes, longitudes).
  1400. #
  1401. # ano: An array of the original input anomalies with dimensions
  1402. # (number of years, latitudes, longitudes).
  1403. #
  1404. # reg: An array containing a map of regression coefficients
  1405. # (number of latitudes, longitudes).
  1406. #
  1407. # Author:
  1408. #
  1409. # Fabian Lienert <flienert@ic3.cat> 15 May 2013.
  1410. #
  1411. nlon <- length(lon); nlat <- length(lat)
  1412. nyr <- dim(ano)[3]; lt <- dim(ano)[4]
  1413. if (all(gmt==F)) {
  1414. # Global mean temperature GMT from HadCRUT, climate explorer.
  1415. # http://climexp.knmi.nl/data/icrutem3_hadsst2_0-360E_-90-90N_n.dat
  1416. T1 <- read.table("/cfu/data/ukmo/hadcrut/icrutem3_hadsst2_0-360E_-90-90N_n.dat",row.names=1)
  1417. XT <- array(NA,dim=c(12,113))
  1418. for (im in 1:12) {
  1419. XT[im,] <- T1[[im]]
  1420. }
  1421. XT[XT==-9.999000e+02] <- NA
  1422. # GMT anomaly wrt 1356 mo, 113 yr.
  1423. tgl.obs <- ts(c(as.vector(XT),rep(NA,120)),deltat=1/12,start=c(1900,1),end=c(2022,12))
  1424. # GMT anomaly wrt verification period.
  1425. tgl.vp.nov.obs <- window(tgl.obs,start=c(yr1,11),end=c(((yr2+lt)),10))
  1426. tgl.vp.nov.obs <- tgl.vp.nov.obs*((yr2-yr1+lt)/113)
  1427. gmt <- aggregate(tgl.vp.nov.obs,FUN=mean)
  1428. }
  1429. o.XY <- array(NA,dim=c(((yr2-yr1+lt)),nlat,nlon))
  1430. if (yrint>1) {
  1431. TP1 <- ano[1,1,,1:(yrint),nlat:1,]
  1432. TP1 <- aperm(TP1, c(2,1,3,4))
  1433. } else {
  1434. TP1 <- ano[1,1,,1,nlat:1,]
  1435. }
  1436. dim(TP1) <- c(nyr*yrint,nlat,nlon)
  1437. o.XY[1:(nyr*yrint),,] <- TP1
  1438. o.XY[((nyr*yrint)+1):((yr2-yr1+lt)),,] <- ano[1,1,nyr,((yrint)+1):lt,nlat:1,]
  1439. if (length(gmt)!=((yr2-yr1+lt))) {
  1440. print(paste('GMT time series has not the same length than the array of anomalies.',length(gmt),((yr2-yr1+lt))))
  1441. stop()
  1442. }
  1443. # Remove trend from the obs for the whole period.
  1444. o.XY.dt <- array(NA,dim=c(((yr2-yr1+lt)),nlat,nlon))
  1445. r.XY <- (o.XY.dt[1,,]*NA)
  1446. for (i in 1:nlon) {
  1447. for (j in 1:nlat) {
  1448. if (!is.na(o.XY[1,j,i])) {
  1449. # Regress anomaly onto GMT and remove it from the anomaly.
  1450. a1 <- o.XY[,j,i]
  1451. # Exclude NAs from linear model estimate, but keep them in the residual time series. (!=$residuals)
  1452. fit <- lm(a1~as.vector(gmt),na.action=na.exclude)
  1453. a1.dt <- residuals(fit)
  1454. o.XY.dt[,j,i] <- a1.dt
  1455. # Regression coefficient map.
  1456. r.XY[j,i] <- coefficients(fit)[[2]]
  1457. }
  1458. }
  1459. }
  1460. # Put detrended obs in verification format.
  1461. ano.dt.ver <- array(NA,dim=c(1,1,nyr,lt,nlat,nlon))
  1462. # Works for forecast start date in each year.
  1463. for (i in 1:nlon) {
  1464. for (j in 1:nlat) {
  1465. d.all <- ts(o.XY.dt[,j,i],deltat=1,start=c(yr1,11),end=c((yr2+lt),10))
  1466. if (!is.na(o.XY[1,j,i])) {
  1467. for (iy in 1:nyr) {
  1468. d.sl <- window(d.all,start=c((yr1+iy*yrint-yrint),11),end=c((yr1+lt+iy*yrint-yrint),10))
  1469. ano.dt.ver[1,1,iy,,((nlat+1)-j),i] <- d.sl
  1470. }
  1471. }
  1472. }
  1473. }
  1474. return(list(ano.dt=o.XY.dt,ano=o.XY,reg=r.XY,ano.dt.ver=ano.dt.ver,gmt=gmt))
  1475. }
  1476. CFU_EOF <- function(lon,lat,ano,neofs=15,corr=FALSE) {
  1477. #
  1478. # Description:
  1479. #
  1480. # Performs an area-weighted EOF analysis using SVD based on a covariance matrix by default,
  1481. # based on a correlation matrix if corr argument is set to TRUE.
  1482. #
  1483. # Arguments:
  1484. #
  1485. # lon: A vector of longitudes.
  1486. #
  1487. # lat: A vector of latitudes.
  1488. #
  1489. # ano: Array of anomalies with dimensions
  1490. # (number of timesteps, number of latitudes, number of longitudes).
  1491. #
  1492. # neofs: Number of modes to be kept. Default = 15
  1493. #
  1494. # corr: based on correlation matrix rather than covariance matrix. Default = FALSE
  1495. #
  1496. # Output:
  1497. #
  1498. # EOFs: An array of EOF patterns normalized to 1 with dimensions
  1499. # (number of modes, number of latitudes, number of longitudes)
  1500. #
  1501. # PCs: An array of principal components with dimensions
  1502. # (number of timesteps, number of modes)
  1503. #
  1504. # Var: Percentage (%) of variance fraction of total variance explained by each mode (number of modes).
  1505. #
  1506. # mask: Mask with dimensions (number of latitudes, number of longitudes)
  1507. #
  1508. # wght: Weights with dimensions (number of latitudes, number of longitudes)
  1509. #
  1510. # Author:
  1511. #
  1512. # Fabian Lienert <flienert@ic3.cat> 29 Oct 2012. Inspired by R. Benestad's EOF() in R-package clim.pact.
  1513. #
  1514. # Lauriane Batte <lauriane.batte@ic3.cat> March 2014. Bug-fixes : 1-reversion of latitudes in the weights
  1515. # 2-correlation matrix was used instead of covariance
  1516. # 3-double use of the weights
  1517. #
  1518. # Virginie Guemas <virginie.guemas@ic3.cat> 17 March 2014. Bug-fixes: 1-weight computation - division by sum of cos(lat)
  1519. # 2-shuffling of EOFs in EOF.2 intermediate vector
  1520. # 3-crash when neofs=1 sorted out
  1521. # 4-crash when neofs>nt sorted out
  1522. #
  1523. # Lauriane Batte <lauriane.batte@ic3.cat> 19 March 2014 : BIG cleanup of code and clarification
  1524. # Reduction of the number of transpositions and bug-fixes associated
  1525. # Remove of the obsolete LINPACK options
  1526. #
  1527. # Virginie Guemas <virginie.guemas@ic3.cat> 11 April 2014.
  1528. # 1 - Bug-fix in dimensions handling
  1529. # EOF composition restitutes now the original field in all cases
  1530. # 2 - Simplification of the convention transpose
  1531. # 3 - Options to use the correlation matrix rather than the covariance matrix
  1532. # 4 - Security checks
  1533. # 5 - New normalization of PCs so that PC*EOF only reconstruct the original file
  1534. # 6 - Weights = sqrt(cos(lat)) for ano so that covariance matrice weighted by cos(lat)
  1535. # 7 - Division of EOF by weights so that the reconstruction is simply EOF * PC
  1536. #
  1537. library(s2dverification)
  1538. nlon <- length(lon); nlat <- length(lat)
  1539. # The two lines below have been moved up for the security checks. Virginie
  1540. dim.dat <- dim(ano)
  1541. ny <- dim.dat[2]; nx <- dim.dat[3]; nt <- dim.dat[1]
  1542. # Security check. Virginie
  1543. if ( ny != nlat ) {
  1544. stop("Inconsistent number of latitudes and input field dimensions")
  1545. }
  1546. if ( nx != nlon ) {
  1547. stop("Inconsistent number of longitudes and input field dimensions")
  1548. }
  1549. # Buildup of the mask. Fabian.
  1550. mask <- ano[1,,]; mask[!is.finite(mask)] <- NA
  1551. mask[is.finite(mask)] <- 1
  1552. # Replace mask of NAs with 0s for EOF analysis. Fabian
  1553. ano[!is.finite(ano)] <- 0
  1554. # Area weighting. Weights for EOF. Fabian. Bug below. Lauriane
  1555. # wght <- array(cos(rev(lats)*pi/180),dim=(c(nlat,nlon)))
  1556. # The reversion of latitudes is not suitable. Lauriane
  1557. wght <- array(cos(lat*pi/180),dim=(c(nlat,nlon)))
  1558. # The sum of the weights should equal to 1. Virginie
  1559. wght <- wght/sum(wght,na.rm=T)
  1560. # We want the covariance matrix to be weigthed by the grid cell area
  1561. # so the anomaly field should be weighted by its square root since
  1562. # the covariance matrix equals transpose(ano) times ano. Virginie.
  1563. wght <- sqrt(wght)
  1564. # Fabian was initially dividing by stdv to get the correlation matrix.
  1565. # Weighting of the anomalies that corresponds to the use of a covariance matrix. Lauriane.
  1566. #for (it in 1:nt) {ano[it,,] <- ano[it,,]*wght/stdv}
  1567. ano <- ano*InsertDim(wght,1,nt)
  1568. # The use of the correlation matrix is done under the option corr. Virginie.
  1569. if (corr==TRUE) {
  1570. stdv <- sd(ano,na.rm=T)
  1571. ano <- ano/InsertDim(stdv,1,nt)
  1572. }
  1573. # Time/space matrix for SVD. Fabian
  1574. dim(ano) <- c(nt,ny*nx)
  1575. dim.dat <- dim(ano)
  1576. # "transposed" means already transposed. Fabian's convention.
  1577. # This is very misleading because the user has actually not transposed its array.
  1578. # Only by chance, the spatial dimension is smaller than the time dimension.
  1579. # I therefore change the convention to:
  1580. # "transpose" means the array needs to be transposed before calling La.svd for
  1581. # computational efficiency because the spatial dimension is larger than the time
  1582. # dimension. This goes with transposing the outputs of LA.svd also. Virginie
  1583. #print(c('dim (nt,npts):',dim.dat))
  1584. #if (dim.dat[2] < dim.dat[1]) {transposed <- TRUE} else {transposed <- FALSE}
  1585. if (dim.dat[2] > dim.dat[1]) {transpose <- TRUE} else {transpose <- FALSE}
  1586. if (transpose) {
  1587. pca <- La.svd(t(ano))
  1588. } else {
  1589. pca <- La.svd(ano)
  1590. }
  1591. # Remove of the obsolete LINPACK cases. Lauriane.
  1592. # The number of output eofs might be lower than 15 if less than 15 grid points are used. Fabian.
  1593. #if (neofs > dim(ano)[2]) {neofs <- dim(ano)[2]}
  1594. # Whatever the spatial or temporal dimension is smaller than neofs, neofs should be bounded. Virginie.
  1595. neofs=min(dim(ano),neofs)
  1596. # The line below by Fabian is not used. Virginie
  1597. #dim.v <- dim(pca$v); dim.u <- dim(pca$v); dim.x <- dim(ano)
  1598. #
  1599. # Lauriane :
  1600. # La.svd conventions: decomposition X = U D t(V)
  1601. # La.svd$u returns U
  1602. # La.svd$d returns diagonal values of D
  1603. # La.svd$v returns t(V) !!
  1604. # The usual convention is PC=U and EOF=V.
  1605. # If La.svd is called for ano (transpose=FALSE case):
  1606. # EOFs: $v
  1607. # PCs: $u
  1608. # If La.svd is called for t(ano) (transposed=TRUE case):
  1609. # EOFs: t($u)
  1610. # PCs: t($v)
  1611. if (transpose) {
  1612. pca.EOFs <- t(pca$u)
  1613. pca.PCs <- t(pca$v)
  1614. #print('transpose')
  1615. #print(c('Dim(pca.EOFs) :',dim(pca.EOFs)))
  1616. #print(c('Dim(pca.PCs) :',dim(pca.PCs)))
  1617. } else {
  1618. pca.EOFs <- pca$v
  1619. pca.PCs <- pca$u
  1620. #print('not transpose')
  1621. #print(c('Dim(pca.EOFs) :',dim(pca.EOFs)))
  1622. #print(c('Dim(pca.PCs) :',dim(pca.PCs)))
  1623. }
  1624. # The numbers of transposition has been reduced. Virginie
  1625. #PC <- pca.PCs[1:neofs,]
  1626. PC <- pca.PCs[,1:neofs]
  1627. EOF <- pca.EOFs[1:neofs,]
  1628. # The lines seem to be at the origin of the lack of recomposition in last version. Virginie.
  1629. #dim(EOF) <- c(neofs,nx,ny)
  1630. #EOF <- aperm(EOF, c(1,3,2))
  1631. dim(EOF) <- c(neofs,ny,nx)
  1632. # To sort out crash when neofs=1. Virginie
  1633. if (neofs==1) {
  1634. PC=InsertDim(PC,2,1)
  1635. #EOF=InsertDim(EOF,1,1) # This is not necessary with line dim(EOF) above. Lauriane
  1636. }
  1637. # Computation of the % of variance associated with each mode. Fabian.
  1638. W <- pca$d[1:neofs]
  1639. tot.var <- sum(pca$d^2)
  1640. Var.eof <- 100 * pca$d[1:neofs]^2/tot.var
  1641. for (e in 1:neofs) {
  1642. # sg is initialized here. It detects the sign of the pattern and latter the pattern is
  1643. # multiply by -1 if necessary to obtain the nominal IPO or PDO. Fabian
  1644. # CFU_EOF should be generic enough to compute any type of EOF. The change of sign of
  1645. # the pattern should be done outside CFU_EOF. Lauriane & Virginie.
  1646. #sg <- 1
  1647. # Factor to normalize the PC.
  1648. eof.pc.sd <- sd(PC[,e])
  1649. # Factor to normalize the EOF.
  1650. eof.patt.nn <- EOF[e,,]*mask
  1651. # Weights have already been taken into account in covariance matrix. Lauriane.
  1652. #eof.patt.ms <- sum(eof.patt.nn^2*t(wght*mask),na.rm=TRUE)/sum(t(wght*mask),na.rm=TRUE)
  1653. eof.patt.ms <- sum(eof.patt.nn^2,na.rm=TRUE)
  1654. # Normalize PC and EOF
  1655. eof.patt <- eof.patt.nn/eof.patt.ms
  1656. # I would rather multiply eof.pc by eof.patt.ms and W[e] so that the user can reconstruct
  1657. # the original field by EOF * PC only (*weight) rather than EOF * PC * (normalization
  1658. # factor that the user does not know) * (eigenvalue that is not straightforward to get back
  1659. # from the % of variance explained) (*weight). The division by sd(pc) is very easy to do
  1660. # outside CFU_EOF in case the user needs a normalized PC but it is more difficult to get
  1661. # back the amplitude of the variability represented by a given mode outside CFU_EOF if
  1662. # we do not output such a muliplied PC multiplied. Virginie.
  1663. #eof.pc <- PC[,e]/eof.pc.sd
  1664. eof.pc <- PC[,e]*eof.patt.ms*W[e]
  1665. # I am also wondering if we should not output the EOF divided by the weight
  1666. # so that the reconstruction is only EOF * PC only rather than EOF * PC * weight
  1667. # since we have multiplied ano by weight. Virginie
  1668. eof.patt <- eof.patt/wght
  1669. # Change the sign of first EOF pattern for PDO, IPO, etc. Fabian
  1670. #if (e==1) if (eof.patt[round(nlon/2),round(nlat/2)] > 0) sg <- -1
  1671. EOF[e,,] <- eof.patt
  1672. PC[,e] <- eof.pc
  1673. }
  1674. return(list(EOFs=EOF,PCs=PC,Var=Var.eof,mask=mask,wght=wght))
  1675. }
  1676. CFU_plotbox <- function(NAO,yr1,yr2,tit='NAO index',tar='DJF',pr.qu=expression('NAO index (PC1) SLP'~(sigma)),obs='ERAInterim',fcsys='EC-Earth 3',file='NAO_fc.ps') {
  1677. ##
  1678. ## Produce time series plot showing the distribution of the forecast members with box-and-whisker plot.
  1679. ##
  1680. ## Description:
  1681. ##
  1682. ## Generates vector graphics file of a forecast box-and-whisker plot time series vs. the observed evolution.
  1683. ## Only works for re-forecasts started each year.
  1684. ##
  1685. ## Usage:
  1686. ##
  1687. ## CFU_plotbox(lon,lat,NAO,yr1,yr2,tit='NAO index',tar='DJF',pr.qu='NAO index (PC1) SLP',
  1688. ## obs='ERAInterim',fcsys='EC-Earth 3',file='NAO_fc.ps')
  1689. ## Arguments:
  1690. ##
  1691. ## NAO: List of obs, forecast arrays of, e.g., the NAO index for each ensemble member, output of CFU_NAO().
  1692. ## yr1: Year of first forecast target period.
  1693. ##
  1694. ## yr2: Year of last forecast target period.
  1695. ##
  1696. ## tit: Title of the Figure.
  1697. ##
  1698. ## tar: Forecast target period.
  1699. ##
  1700. ## pr.qu: Prognostic quantity.
  1701. ##
  1702. ## obs: Observational dataset.
  1703. ##
  1704. ## fcsys: Climate forecast system.
  1705. ##
  1706. ## file: Name of the postscript output file.
  1707. ##
  1708. ## Output:
  1709. ##
  1710. ## Vector graphics file of a forecast box-and-whisker plot time series vs. the observed evolution.
  1711. ##
  1712. ## Author:
  1713. ##
  1714. ## Fabian Lienert <flienert@ic3.cat> 09 Aug 2013.
  1715. ##
  1716. ## Modifications:
  1717. ##
  1718. ## Lauriane Batte <lauriane.batte@ic3.cat> 18 Mar 2014
  1719. ## 1. No normalization of the indices (already taken care of
  1720. ## in previous functions)
  1721. ## 2. Taking into account NA cases (missing data)
  1722. ##
  1723. ##
  1724. nyr <- dim(NAO$NAOO.ver)[3]
  1725. postscript(file,horizontal=T)
  1726. ## Observed time series.
  1727. pc.o <- ts(NAO$NAOO.ver[1,1,],deltat=1,start=yr1,end=yr2)
  1728. ## Normalization of obs, forecast members.
  1729. ## This has already been done in CFU_MODE
  1730. # pc.o <- pc.o/sd(pc.o)
  1731. # sd.fc <- apply(NAO$NAOF.ver[1,,],c(1),sd)
  1732. # NAO$NAOF.ver[1,,] <- NAO$NAOF.ver[1,,]/sd.fc
  1733. ## Produce plot.
  1734. par(mar = c(5, 6, 4, 2))
  1735. boxplot(NAO$NAOF.ver[1,,],add=F,main=paste(tit,tar),ylab='',xlab='',
  1736. col='red',lwd=2,t='b',axes=F,cex.main=2,
  1737. ylim=c(-max(abs(c(NAO$NAOF.ver[1,,],pc.o)),na.rm=TRUE),max(abs(c(NAO$NAOF.ver[1,,],pc.o)),na.rm=TRUE)))
  1738. lines(1:nyr,pc.o,lwd=3,col='blue'); abline(h=0,lty=1)
  1739. legend('bottomleft',c(obs,fcsys),lty=c(1,1),lwd=c(3,3),pch=c(NA,NA),
  1740. col=c('blue','red'),horiz=T,bty='n',cex=2.2)
  1741. axis(1,c(1:nyr),NA,cex.axis=2.0)
  1742. axis(1,seq(1,nyr,by=1),seq(start(pc.o)[1],end(pc.o)[1],by=1),cex.axis=2.0)
  1743. mtext(1,line=3,text=tar,cex=1.9)
  1744. mtext(3,line=-2,text=paste(' AC =',round(cor(pc.o,apply(NAO$NAOF.ver[1,,],c(2),mean,na.rm=TRUE)),2)),cex=1.9,adj=0)
  1745. axis(2,cex.axis=2.0)
  1746. mtext(2,line=3,text=pr.qu,cex=1.9)
  1747. box()
  1748. dev.off()
  1749. }
  1750. CFU_NAO <- function(lon,lat,ano.f,ano.o,fcavt=2:4,fcsys=1) {
  1751. ##
  1752. ## Compute the North Atlantic Oscillation (NAO) index based on the leading EOF of sea level pressure (SLP) anomalies.
  1753. ## The PCs are obtained by projecting the forecast and oberved anomalies onto the observed EOF pattern.
  1754. ## By default (fcavt=2:4) CFU_NAO() computes the NAO index for 1-month lead seasonal forecasts that can be
  1755. ## plotted with CFU_boxplot().
  1756. ##
  1757. ## Description:
  1758. ##
  1759. ## Returns cross-validated PCs of the NAO index for forecast (ano.f) and observations (ano.o) based on the
  1760. ## observed leading EOF pattern.
  1761. ##
  1762. ## Usage:
  1763. ##
  1764. ## CFU_NAO(lon,lat,ano.f,ano.o,fcavt=2:4,fcsys=1)
  1765. ##
  1766. ## Arguments:
  1767. ##
  1768. ## lon: A vector of longitudes.
  1769. ##
  1770. ## lat: A vector of latitudes.
  1771. ##
  1772. ## ano.f: Array of North Atlantic SLP (0-60N, 80W-0) forecast anomalies from CFU_ano() with dimensions
  1773. ## (number of forecast systems, ensemble members, start years, forecast months, latitudes, longitudes).
  1774. ##
  1775. ## ano.o: Array of North Atlantic SLP (0-60N, 80W-0) forecast anomalies from CFU_ano() with dimensions
  1776. ## (number of forecast systems, ensemble members, start years, forecast months, latitudes, longitudes).
  1777. ##
  1778. ## fcavt: A vector of time steps to average across defining the target period.
  1779. ##
  1780. ## fcsys: Number of the forecast system.
  1781. ##
  1782. ##
  1783. ## Output:
  1784. ##
  1785. ## NAOF.ver: Array of forecast NAO index in verification format
  1786. ## (number of forecast systems, ensemble members, start years, forecast months).
  1787. ##
  1788. ## NAOO.ver: Array of observed NAO index in verification format
  1789. ## (1, 1, number of start years, forecast months).
  1790. ##
  1791. ## Author:
  1792. ##
  1793. ## Fabian Lienert <flienert@ic3.cat> 08 Aug 2013.
  1794. ##
  1795. ## Lauriane Batte <lauriane.batte@ic3.cat> March 2013.
  1796. ##
  1797. ## Virginie Guemas <virginie.guemas@ic3.cat> 17 March 2013. Removing the rotation.
  1798. ##
  1799. nlon <- length(lon); nlat <- length(lat)
  1800. nyr <- dim(ano.f)[3]; nfc <- dim(ano.f)[2]; lt <- dim(ano.f)[4]
  1801. # A security check should be added nlon==nx & nlat==ny. Virginie.
  1802. nlonr <- ceiling(length(lon[lon<180 & lon>=0]))
  1803. ## Rotate field to start west of 0 longitude. Fabian.
  1804. # This rotation should be done after the projection to output the
  1805. # NAO pattern only. Virginie.
  1806. #ano.o.sh <- (ano.o*NA)
  1807. #ano.o.sh[1,1,,,,] <- ano.o[1,1,,,,c((nlonr+1):nlon,1:nlonr)]
  1808. #ano.f.sh <- (ano.f*NA)
  1809. #ano.f.sh[1,,,,,] <- ano.f[fcsys,,,,,c((nlonr+1):nlon,1:nlonr)]
  1810. #remove(ano.o,ano.f);
  1811. ano.f.sh <- ano.f; ano.o.sh=ano.o
  1812. gc()
  1813. ## Target period mean. Fabian.
  1814. # The same operations can be done in half the lines using s2dverification. Virginie
  1815. # Also this operation could be more general using CFU_season. Virginie
  1816. o1 <- apply(ano.o.sh[1,1,,fcavt,,],c(1,3,4),mean,na.rm=T)
  1817. dim(o1) <- c(1,1,nyr,1,nlat,nlon)
  1818. f1 <- apply(ano.f.sh[1,,,fcavt,,],c(1,2,4,5),mean,na.rm=T)
  1819. dim(f1) <- c(1,nfc,nyr,1,nlat,nlon)
  1820. ## Cross-validated PCs. Fabian.
  1821. # This should be extended to nmod and nlt by simple loops. Virginie
  1822. NAOF.ver <- array(NA,c(1,nfc,nyr))
  1823. NAOO.ver <- array(NA,c(1,1,nyr))
  1824. for (iy in 1:nyr) {
  1825. ## Observed EOF excluding one forecast start year. Fabian
  1826. # Without the rotation and only 1 EOF. Virginie
  1827. # OEOF <- CFU_EOF(c(lon[c((nlonr+1):nlon)]-360,lon[1:nlonr]),lat,o1[1,1,-iy,1,,],neofs=3)
  1828. OEOF <- CFU_EOF(lon,lat,o1[1,1,-iy,1,,],neofs=1)
  1829. ## Correct polarity of pattern. Fabian
  1830. sign <- 1; if ( 0 < mean(OEOF$EOFs[1,,round(2*nlat/3)],na.rm=T) ) {sign <- -1}
  1831. OEOF$EOFs <- OEOF$EOFs*sign; OEOF$PCs <- OEOF$PCs*sign
  1832. # There should be an option that allows compute the model EOFs instead of only projecting on the observed EOFS. Virginie
  1833. # for (ifc in 1:nfc) {
  1834. # FEOF <- CFU_EOF(lon,lat,f1[1,ifc,,1,,],neofs=1)
  1835. # NAOF.ver[1,ifc,]=FEOF$PCs
  1836. # }
  1837. ## Project forecast anomalies. Fabian
  1838. # Without the rotation. Virginie
  1839. #PCF <- CFU_MODE(c(lon[c((nlonr+1):nlon)]-360,lon[1:nlonr]),lat,f1,OEOF,mode=1)
  1840. PCF <- CFU_MODE(lon,lat,f1,OEOF,mode=1)
  1841. # for (imo in 1:nmod) { for (il in 1:lt) {NAOF.ver[imo,,iy,il] <- PCF$PC.ver[imo,,iy,imo]}}
  1842. NAOF.ver[1,,iy] <- PCF$PC.ver[1,,iy,1]
  1843. ## Project observed anomalies. Fabian.
  1844. # Without the rotation. Virginie.
  1845. #PCO <- CFU_MODE(c(lon[c((nlonr+1):nlon)]-360,lon[1:nlonr]),lat,o1,OEOF,mode=1)
  1846. PCO <- CFU_MODE(lon,lat,o1,OEOF,mode=1)
  1847. ## Keep PCs of excluded forecast start year. Fabian.
  1848. NAOO.ver[1,1,iy] <- PCO$PC.ver[1,1,iy,1]
  1849. }
  1850. # Here there could be a rotation of the pattern. Virginie
  1851. # OEOF$EOFs[1,,] <- OEOF$EOFs[1,c((nlonr+1):nlon,1:nlonr),]
  1852. # The problem with this rotation is that the user does not have the corresponding longitudes anymore.
  1853. # I don't think if should be rotated at all then. Virginie
  1854. return(list(NAOF.ver=NAOF.ver,NAOO.ver=NAOO.ver,OEOF=OEOF))
  1855. }