R-Modified Mann-Kendall 趋势分析(改进后MK趋势分析)

0. 问题导入

很多情况下,我们需要研究时间序列,如降水,气温,GDP,人口等要素历史或未来的演变规律。特别是面向全球的研究,我们往往需要将空间的时空矩阵通过计算趋势值,进而压缩到一张图上来展示我们所感兴趣要素的时空变异规律(图1)。

图1 数据结构及计算流程

本文介绍的改进后的Mann-Kendall趋势分析方法已被广泛应用到分析时间序列演变规律过程中的应用(文末附改进后MK趋势分析方法函数)。

1. 示例数据

本次示例数据百度云链接如下,可点击下载。
全球SSP情景GDP数据说明
全球SSP情景GDP数据

2. 数据导入与预处理

在分析计算空间上点的MK趋势之前,我们首先得确定其在真个时间区间内有变化。若无变化,则需要将其去掉,否则计算过程会报错。

那么,如何事先确定一个时间序列是否有变化呢?

解决思路:

  1. 计算整个序列的平均值
  2. 计算整个序列的最大值
  3. 做差,若差为0,则该时间序列无在研究的时间范围内无变化。

特别注意,由于该csv 文件过大(60MB左右),请参考Rstudio-是不是发现你的Rstudio读取大的CSV越来越慢了这篇文章,采用fread函数导入数据。

gdp = fread('L:\\JianShu\\2019-12-05\\data_trend\\gdp_world_ssp1.csv')
gdp = as.matrix(gdp)
gdp = apply(gdp,2,as.numeric)
# 提出经纬度信息
long = gdp[,1]
lat = gdp[,2]
gdp = gdp[,-c(1,2)]
# 预判时间序列是否有变化
mean_gdp = apply(gdp,1,mean)
max_gdp = apply(gdp,1,max)  
diff_gdp = max_gdp - mean_gdp
#筛选出无变化时间序列的行号,并将其从gdp 矩阵中删除
index_non = which(diff_gdp ==0)
gdp = gdp[-index_non,]
long = long[-index_non]
lat  = lat[-index_non]

3. 计算每个空间点改进后的MK趋势值(该过程十分耗时)

mmk_cal <- function(x){
  temp = mmkTrend(x)$Zc
  return(temp)
}
system.time(mmk_gdp <- apply(gdp,1,mmk_cal))

4. MK趋势值空间可视化

pl_df = data.frame(long = long,lat = lat,mmk = mmk_gdp)

pl_df_m = melt(pl_df,c('long','lat'))
pl_df_m$cuts = cut(pl_df_m$value,breaks = c(0,2,4,6,8,Inf))

fontsize = 12

legend_set<-theme(legend.background = element_rect(fill='white'),
                  legend.key = element_blank(),
                  legend.key.size = unit(3,'lines'),
                  legend.key.height=unit(0.1,"inches"),#图例分类符号高度
                  legend.key.width=unit(0.5,"inches"),#图例符号的宽度
                  legend.text=element_text(colour="black",size=fontsize,face = 'bold'),#图例分类标签设置
                  legend.text.align=0,#0左,1右,0.5居中, 图例分类标签的对齐方式
                  #legend.title=element_text(colour="black",size=15,face='bold'),#图例标题设置
                  legend.title = element_blank(),
                  legend.title.align=1,#图例标题对齐方式
                  legend.position=c('bottom'),#"none","left","right","bottom","top",or 
                  # two-element numeric vector,(0,0)-(1,1)
                  legend.direction="horizontal",#"vertical" 图例排列方向
                  legend.justification=c('center'),#"center" or two-element numeric vector
                  legend.box="vertical",#"horizontal",对图例的排列方式
                  legend.box.just="top"#多图例的居中方式
                  ,plot.background = element_rect(color='transparent'))

mycolor = colorRampPalette(brewer.pal(11,'Spectral'))(5)
color_group = paste0('a',1:12)

main_plot = ggplot()+
  geom_hline(aes(yintercept = 50),linetype = 'dashed',alpha = 0.5,lwd = 0.5,color = 'black')+
  geom_hline(aes(yintercept = 0),linetype = 'dashed',alpha = 0.5,lwd = 0.5,color = 'black')+
  geom_hline(aes(yintercept = -50),linetype = 'dashed',alpha = 0.5,lwd = 0.5,color = 'black')+
  geom_vline(aes(xintercept = 0),linetype = 'dashed',alpha = 0.5,lwd = 0.5,color = 'black')+
  geom_vline(aes(xintercept = -100),linetype = 'dashed',alpha = 0.5,lwd = 0.5,color = 'black')+
  geom_vline(aes(xintercept = 100),linetype = 'dashed',alpha = 0.5,lwd = 0.5,color = 'black')+
  geom_tile(data = pl_df_m,aes(x = long,y = lat, fill = cuts))+
  theme(panel.background = element_rect(fill = 'transparent',color = 'black'),
        axis.text = element_text(face='bold',colour='black',size=fontsize,hjust=.5),
        axis.title = element_text(face='bold',colour='black',size=fontsize,hjust=.5),
        legend.position=c('bottom'),
        legend.direction = c('horizontal'))+
  scale_fill_manual(values = mycolor)+
  coord_fixed(1.3)+
  geom_hline(aes(yintercept = 0),linetype = 'dashed',alpha = 0.5,lwd = 0.5,color = 'black')+
  guides(fill=guide_legend(nrow=1))+
  xlab('Longitude')+
  ylab('Latitude')

main_plot
图2 全球GDP-MMK趋势分析结果图

5. 所用软件包

library('ggplot2')
library('data.table')
library('RColorBrewer')
library('reshape2')

6. 改进后Mann-Kendall趋势分析函数

mmkTrend <-
  function(x, ci = .95) {
    x = x
    z = NULL
    z0 = NULL
    pval = NULL
    pval0 = NULL
    S = 0
    Tau = NULL
    essf = NULL
    ci = ci
    if (is.vector(x) == FALSE) {
      stop("Input data must be a vector")
    }
    if (any(is.finite(x) == FALSE)) {
      x[-c(which(is.finite(x) == FALSE))] -> x
      warning("The input vector contains non-finite numbers. An attempt was made to remove them")
    }
    n <- length(x)
    for (i in 1:(n-1)) {
      for (j in (i+1):n) {
        S = S + sign(x[j]-x[i])
      }
    }
    acf(rank(lm(x ~ I(1:n))$resid), lag.max=(n-1), plot=FALSE)$acf[-1] -> ro
    qnorm((1+ci)/2)/sqrt(n) -> sig
    rep(NA,length(ro)) -> rof
    for (i in 1:(length(ro))) {
      if(ro[i] > sig || ro[i] < -sig) {
        rof[i] <- ro[i]
      } else {
        rof[i] = 0
      }
    }
    2 / (n*(n-1)*(n-2)) -> cte
    ess=0
    for (i in 1:(n-1)) {          
      ess = ess + (n-i)*(n-i-1)*(n-i-2)*rof[i]
    }
    essf = 1 + ess*cte
    var.S = n*(n-1)*(2*n+5)*(1/18) 
    if(length(unique(x)) < n) {
      unique(x) -> aux
      for (i in 1:length(aux)) {
        length(which(x == aux[i])) -> tie
        if (tie > 1) {
          var.S = var.S - tie*(tie-1)*(2*tie+5)*(1/18)  
        }
      }
    }
    VS = var.S * essf            
    if (S == 0) {
      z = 0
      z0 = 0
    }
    if (S > 0) {
      z = (S-1)/sqrt(VS) 
      z0 = (S-1)/sqrt(var.S)
    } else {
      z = (S+1)/sqrt(VS) 
      z0 = (S+1)/sqrt(var.S)
    }      
    pval = 2*pnorm(-abs(z))
    pval0 = 2*pnorm(-abs(z0)) 
    Tau = S/(.5*n*(n-1))
    rep(NA, times=(n^2-n)/2) -> V
    k = 0
    for (i in 2:n) {
      for (j in 1:i) {
        k = k+1
        V[k] = (x[i]-x[j])/(i-j)
        
      }
    }
    median(na.omit(V)) -> slp
    return(list("Z" = z0, "p.value" = pval0, "Zc" = z, "Corrected p.value" = pval, "tau" = Tau, "N/N*s" = essf, "Sen's Slope" = slp))
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容