Quantcast
Channel: グラフ - 廿TT

[googleAnalyticsR]再発事象のカプラン・マイヤープロット

$
0
0

survrec っていうパッケージもあるらしいけど慣れてないので survival を使います。

f:id:abrahamcow:20171017070824p:plain

どのチャネルが再来訪しやすいのか、何日くらいの間隔で何パーセントくらい戻ってくるのかがわかります。

library(googleAnalyticsR)library(tidyverse)library(survival)library(ggfortify)#####
ga_auth()
account_list <- ga_account_list()
ga_id <- account_list$viewId[3]

gadata <-
  google_analytics_4(ga_id,
                     date_range = c("2017-09-01","2017-09-30"),
                     metrics = c("sessions"),
                     dimensions = c("date","channelGrouping","dimension1"),
                     max =20000)

censor <-function(x){
  n<-length(x)
  out <-rep(1,n)
  out[n]<-0
  out
}

gadata2 <- gadata %>% 
  group_by(date,dimension1)%>% 
  summarise(channelGrouping=last(channelGrouping))%>% 
  group_by(dimension1)%>% 
  mutate(interval=as.integer(diff(c(date,as.Date("2017-10-01")))))%>% 
  mutate(d=censor(interval))%>% 
  ungroup()

sf1 <-survfit(Surv(interval,d)~channelGrouping,data=gadata2,conf.int=0.6)

autoplot(sf1,fun="event",surv.size=1,censor =FALSE,conf.int.alpha =0.2)+
  ylab("retention")+
  guides(colour=guide_legend(title ="channelGrouping"),
         fill=guide_legend(title ="channelGrouping"))

"dimension1"については【GTM版】GoogleアナリティクスのClientIDを取得する方法 | SEM Technologyを参照。


スパークライン風のカプランマイヤープロット

$
0
0

層分けが多くなると凡例と見比べるのが大変なのでなんとかしたいと思った。

普通にプロットする。

f:id:abrahamcow:20171114233556p:plain

はい。

スパークライン風にプロットするとこんな感じかなあ。

f:id:abrahamcow:20171114233838p:plain

累積イベント発生率が25%に達する点、50%に達する点、75%に達する点に赤っぽい丸、最後のイベント発生に黒い丸をうっている。

また背景のグレーの帯は25%〜75%の範囲を示している。

以下 R の コードです。

library(survival)library(tidyverse)library(survminer)
lung2 <- lung %>% 
  filter(ph.ecog!=3)
sf <-survfit(Surv(time,status)~sex+ph.ecog,data=lung2)

ggsurvplot(sf,fun ="event")

sf_df <-data_frame(time=sf$time,event=1-sf$surv,strata=rep(sub("treat=","",names(sf$strata)),sf$strata))

strata=cumsum(sf$strata)
max_df<-data.frame(strata=names(strata),
                   time=sf$time[strata],
                   event=1-sf$surv[strata])

sf_q <-quantile(sf,probs = c(0.25,0.5,0.75))

q_df<-sf_q$quantile %>% 
  as_data_frame()%>% 
  mutate(strata=rownames(sf_q$quantile))%>% 
  gather(q,time,-strata)%>% 
  mutate(event=as.numeric(q)/100)

spark <-   theme_classic(16)+
  theme(axis.title=element_blank(),
        axis.line.y = element_blank(),
        panel.border = element_rect(color ="black",fill=NA),
        strip.text.x = element_blank(),
        strip.text.y = element_text(angle =0),
        strip.background = element_blank())

ggplot(sf_df,aes(x=time,y=event))+
  annotate("rect",xmin=0,xmax=max(sf$time),ymin=0.25,ymax=0.75,
           alpha=0.35,fill="gray")+
  geom_step()+
  geom_point(data=max_df)+
  scale_y_continuous(breaks=c(0.25,0.75))+
  geom_point(data=q_df,aes(x=time),colour="salmon",size=2)+
  facet_grid(strata~.)+
  spark

Rによる打ち切りデータの箱ひげ図

$
0
0

右打ち切りのあるデータを視覚化するのにはカプラン・マイヤープロットが便利です。

しかし層の数が多くなると判例と見比べるのが大変になる。

f:id:abrahamcow:20180123230036p:plain

そこである程度情報を落としてプロットしてもいいかなと思い、打ち切りデータの箱ひげ図を提案にします。

make_q_df.R · GitHub


興味があるのは生存時間の長さなので、箱ひげ図の髭の下、箱の下、中央の線、箱の上、髭の上にそれぞれ10%の患者が死亡した時点、25%の患者が死亡した時点、50%の患者が死亡した時点、75%の患者が死亡した時点、90%の患者が死亡した時点を対応させてプロットしたのが以下の図です。

f:id:abrahamcow:20180123230439p:plain

四角い枠が観測期間の終了を示しています。

実用性あるか等、コメントいただけると嬉しいです。

以下 R のコードです。

library(survival)library(tidyverse)source("https://gist.githubusercontent.com/abikoushi/db838b6477b0b612d4da1343a0fa3f46/raw/a6c0a6b8a24d1769c070156de227f678026651c7/make_q_df.R")

lung2 <- lung %>% 
  filter(ph.ecog!=3)
sf <-survfit(Surv(time,status)~sex+ph.ecog,data=lung2)
q_df<-make_q_df(sf)

plot(sf,col=1:6)
legend("topright",legend = names(sf$strata),col=1:6,lty=1)

ggplot(q_df,aes(x=strata))+
  geom_boxplot(aes(lower=q0.25,upper=q0.75,middle=q0.5,ymin=q0.1,ymax=q0.9),stat ="identity")+
  scale_y_continuous(limits = c(0,max(lung2$time[lung2$status==1])),
                     expand = c(0,0))+
  coord_flip()+
  xlab("")+ylab("")+
  theme_rect()

abrahamcow.hatenablog.com

abrahamcow.hatenablog.com

Rによる打ち切りデータのヒストグラム (ggplot2)

$
0
0

生存関数のプロットは便利だけど密度関数のプロットと比べると分布の形状を把握しにくい。

そこで打ち切りデータのヒストグラムというのが提案されている(Huzurbazar, A. V. (2005). A Censored Data Histogram. Communications in Statistics - Simulation and Computation, 34 : pp. 113-120. http://www.tandfonline.com/doi/abs/10.1081/SAC-200047089

なんらかの方法で生存率が求まれば適当な幅 bw で生存率の差分を取り、ビンの幅で割ることで密度関数のノンパラメトリックな推定量を得ることができる。

それを関数化しました(make_df4hist · GitHub)。

シミュレーションで右打ち切りのデータを作り、密度関数のカーブと合わせてプロットすると、だいたい一致していることがわかる。

library(survival)library(tidyverse)source("https://gist.githubusercontent.com/abikoushi/b9c56028929dc0720e27e36d784a027e/raw/069dfd04ea76885b1186c93eefb75f928cdf6270/make_df4hist")
set.seed(1); x <- rweibull(10000,2,2)
d <- x<2
x2 <- ifelse(d,x,2)

sf_test <- survfit(Surv(x2,d)~1)
bw <-0.2
dfhisttest <- make_df4hist(sf_test,bw)

p_test <- ggplot(dfhisttest,aes(x=midtime,y=density))+
  geom_col(fill="white",colour="black",width = bw)+
  stat_function(fun = dweibull, args =list(shape=2,scale=2))

print(p_test)

f:id:abrahamcow:20180124103039p:plain

パラメトリックモデルの当てはまりを見るのなんかにも使えるかもしれない。

lung2 <- lung %>% 
  filter(ph.ecog!=3)%>% 
  mutate(sex=sex-1)

sf <-survfit(Surv(time,status)~sex+ph.ecog,data=lung2)
sr <-survreg(Surv(time,status)~sex+ph.ecog,data=lung2)

bw <-120
df4hist <- make_df4hist(sf,bw)

dfparam <- df4hist %>% 
  mutate(sex=as.integer(sex),ph.ecog=as.integer(ph.ecog))%>% 
  group_by(strata)%>% 
  summarise(m=1/sr$scale,eta=first(exp(sr$coefficients[1]+sr$coefficients[2]*sex+sr$coefficients[3]*ph.ecog)))

xv <- seq(0, max(sf$time), len=100)
dfdens <- do.call(rbind,lapply(1:nrow(dfparam),function(i){
  with(dfparam[i,],data.frame(strata, x=xv, y=dweibull(xv,shape = m, scale = eta),stringsAsFactors =FALSE))}))

p2<-ggplot(df4hist)+
  geom_col(aes(x=midtime,y=density),width = bw,fill="white",colour="black")+
  geom_line(data = dfdens,aes(x=x,y=y),colour="royalblue",size=1)+
  facet_wrap(~strata)+
  xlab("elapsed time")
print(p2)

f:id:abrahamcow:20180124103020p:plain

abrahamcow.hatenablog.com

abrahamcow.hatenablog.com

[R+Google アナリティクス]リセンシーどこで切るチャート

$
0
0

アクセス解析の分野ではユーザーの離脱率や維持率を把握したいというニーズがあります。しかし、離脱するユーザーは単にサイトへの訪問を止めるだけで、わざわざ離脱を申告することはめったにありません。このような場合、例えば 「3ヶ月訪問がなければ離脱したと判断する」などのルールを決めることがあります。この「離脱した」と判断する区切りは、3ヶ月がいいのか、2ヶ月がいいのか、4ヶ月がいいのか、難しいところです。

その判断をサポートするために以下のようなグラフを書いてみてはいかがでしょうか。

  1. データセットを学習期間と評価期間に分ける
  2. 閾値を定める
  3. 学習期間で閾値を超えた日数訪問しなかったユーザーを「離脱」、それ以外を「維持」と判断
  4. 維持と判断されたユーザーが、評価期間に再訪問があったユーザーの内、何パーセントかを数える(active)
  5. 同様、維持と判断されたユーザーが、評価期間に再訪問がなかったユーザーの内、何パーセントかを数える(inactive)
  6. 閾値をいろいろ変えて同じことを繰り返す
  7. 閾値を横軸に、active と inactive を縦軸にとりプロット

f:id:abrahamcow:20180209214723p:plain

この図をみると、たとえば閾値を25日とした場合、維持ユーザーの50%強にリーチできる一方で、離脱ユーザーの25%強を維持ユーザーと判断してしまうことがわかります。

active をリターン、inactive をロスと考えると、目標とするリターンを得るためにはどの程度のロスがあるか、あるいはロスをある水準に抑えたい場合にどの程度のリターンが得られるか、大雑把に把握して閾値を決めることができます。

リターンやロスに特に指針がない場合は、active と inactive の差が最大になる点を閾値とすればよいでしょう。

上の図はGoogleアナリティクスのデータから持ってきたもので、2017-07-01から2017-9-30を学習期間、2017-10-01から2017-12-31を評価期間としています。

図を書いた R のコードを載せます。"dimension1"にはユーザーidが入るように設定しています。

library(googleAnalyticsR)library(tidyverse)#####
ga_auth()
account_list <- ga_account_list()
ga_id <- account_list$viewId[3]

gadata <-
  google_analytics_4(ga_id,
                     date_range = c("2017-07-01","2017-12-31"),
                     metrics = c("sessions"),
                     filtersExpression ="ga:userType==Returning Visitor",
                     dimensions = c("date","dimension1","channelGrouping"),
                     max =15000)
gadata_a <- dplyr::filter(gadata,date<=as.Date("2017-9-30"))
gadata_b <- dplyr::filter(gadata,date>as.Date("2017-9-30"))

uid_b <-unique(gadata_b$dimension1)

lastdate <-gadata_a %>% 
  group_by(dimension1)%>% 
  summarise(date=date[which.max(date)],channelGrouping=channelGrouping[which.max(date)])

seq_d <-seq.Date(as.Date("2017-07-01"),as.Date("2017-09-30"),by="1 day")

dflast <-data_frame(date=seq_d)%>% 
  left_join(lastdate,by ="date")%>% 
  mutate(Recency=as.integer(as.Date("2017-09-30")-date))

dfR <- dflast %>% 
  group_by(Recency)%>% 
  summarise(active=sum(dimension1 %in% uid_b),
            inactive=sum(!(dimension1 %in% uid_b))) 


pR<-ggplot(dfR,aes(x=Recency))+
  geom_step(aes(y=cumsum(active)/sum(active),colour="active"),size=1)+
  geom_step(aes(y=cumsum(inactive)/sum(inactive),colour="inactive"),size=1)+
  theme_gray(18)+
  theme(legend.title = element_blank())+
  ylab("")+
  scale_y_continuous(labels = scales::percent)
print(pR)#ggsave("~/Desktop/pR.png",pR)

ユーザーを層別したい場合は以下のようにするとよいでしょう。

下図は直近訪問時のチャネルごとにユーザーを分けて active と inactive をプロットしたものです。

f:id:abrahamcow:20180209221322p:plain

dfR2 <- dflast%>%
  dplyr::filter(channelGrouping !="(Other)")%>% 
  group_by(Recency,channelGrouping)%>% 
  summarise(active_n=sum(dimension1 %in% uid_b),
            inactive_n=sum(!(dimension1 %in% uid_b)))%>%
  group_by(channelGrouping)%>% 
  arrange(Recency)%>% 
  mutate(active=cumsum(active_n)/sum(active_n),
         inactive=cumsum(inactive_n)/sum(inactive_n))%>% 
  gather(state,proportion,-Recency,-channelGrouping,-active_n,-inactive_n)

pR2<-ggplot(dfR2,aes(x=Recency))+
  geom_step(aes(y=proportion,colour=state),size=1)+
  facet_wrap(~channelGrouping)+
  theme_gray(18)+
  theme(legend.title = element_blank())+
  ylab("")+
  scale_y_continuous(labels = scales::percent)
print(pR2)#ggsave("~/Desktop/pR2.png",pR2)

色付き棒グラフ行列(ggplot2)

$
0
0

『StanとRでベイズ統計モデリング』12章のデータを使います

StanとRでベイズ統計モデリング (Wonderful R)

StanとRでベイズ統計モデリング (Wonderful R)

GitHub - MatsuuraKentaro/RStanBook: 『StanとRでベイズ統計モデリング』のサポートページです.

16×24のプレートの各位置に異なる処置を施して目的変数 Y を観測したデータです。

ふつうのヒートマップで図示するとこう。

f:id:abrahamcow:20180214012837p:plain

これだとどの位置にどの処置を施したかはわかりません。

色付き棒グラフ行列で図示するとこうなります。

f:id:abrahamcow:20180214013113p:plain

色が処置を表します。

目的変数Yと説明変数(処置)を同時に見ることができます、そんなにいろんな変数を同時に見る必要があるかという点はさておき。

以下に R のコードを載せます。

library(tidyverse)

dat <-read_csv("https://raw.githubusercontent.com/MatsuuraKentaro/RStanBook/master/chap12/input/data-2Dmesh.txt",
               col_names =FALSE)
dat_d <-read_csv("https://raw.githubusercontent.com/MatsuuraKentaro/RStanBook/master/chap12/input/data-2Dmesh-design.txt",
                 col_names =FALSE)
dat2 <-dat %>% 
  set_names(1:24)%>% 
  mutate(i=row_number())%>% 
  gather(j,Y,-i)%>% 
  mutate(j=as.numeric(j))
dat_d2 <-dat_d %>% 
  set_names(1:24)%>% 
  mutate(i=row_number())%>% 
  gather(j,TID,-i)%>% 
  mutate(j=as.numeric(j))
dat_all <-left_join(dat2,dat_d2,by=c("i","j"))

p_tile <-ggplot(dat_all,aes(x=i,y=j,fill=Y))+
  geom_tile()+
  scale_y_reverse()

print(p_tile)#ggsave("~/Desktop/tile.png",p_tile)

theme_spark <-function(base_size =11, base_family =""){
  theme_grey(base_size = base_size, base_family = base_family)%+replace%
    theme(panel.background = element_rect(fill ="white",colour =NA),
          panel.grid =element_blank(),
          axis.line.y = element_blank(),
          panel.border = element_blank(),
          strip.text.x = element_blank(),
          strip.text.y = element_text(angle =0),
          strip.background = element_blank())}

p_col <-ggplot(dat_all,aes(x=i,y=Y,fill=factor(TID)))+
  geom_col()+
  facet_grid(j~.)+
  scale_y_continuous(breaks = c(0,10))+
  theme_spark()+
  theme(legend.position ="none")

print(p_col)#ggsave("~/Desktop/col.png",p_col)

色付きミニ棒グラフ(searchConsoleR)

$
0
0

色付き棒グラフ行列(ggplot2) - 廿TTで書いた theme_spark でもうちょっとあそんでみる。

theme_spark <-function(base_size =11, base_family =""){
  theme_grey(base_size = base_size, base_family = base_family)%+replace%
    theme(panel.background = element_rect(fill ="white",colour =NA),
          panel.grid =element_blank(),
          axis.line.y = element_blank(),
          panel.border = element_blank(),
          strip.text.x = element_blank(),
          strip.text.y = element_text(angle =0),
          strip.background = element_blank())}

サーチコンソールのデータの図示。

f:id:abrahamcow:20180214090628p:plain

高さでインプレッション、色でクリック率を表して、トータルのインプレッションが多い順に並べている。

library(tidyverse)library(searchConsoleR)

scr_auth()
sc_websites <- list_websites()

scdata <- search_analytics(sc_websites[1,1],
                           dimensions = c("date","page"),
                           rowLimit =50000)

scdata2 <-scdata %>% 
  group_by(page)%>% 
  summarise(totalCTs=sum(clicks),totalIMPs=sum(impressions))%>% 
  dplyr::filter(totalIMPs>=100*90)%>% 
  left_join(scdata,by="page")%>% 
  mutate(pagePath=sub("http://abrahamcow.hatenablog.com/","",page))%>% 
  arrange(date,desc(totalIMPs))%>% 
  mutate(pagePath=factor(pagePath,levels=pagePath[!duplicated(pagePath)]))

p1<-ggplot(scdata2,aes(x=date,y=impressions,fill=ctr))+
  geom_col(width=1)+
  facet_grid(pagePath~.,scales ="free_y")+
  scale_fill_continuous(labels=scales::percent)+
  theme_spark()

print(p1)

統計グラフの本よみたい。おすすめあったら教えて下さい。

The Visual Display of Quantitative Information

The Visual Display of Quantitative Information

The Grammar of Graphics (Statistics and Computing)

The Grammar of Graphics (Statistics and Computing)

The Elements of Graphing Data

The Elements of Graphing Data

Fundamentals of Data Visualization

精神疾患の軽症化

$
0
0


精神科に新規で入院する患者数は増加傾向にあるようです。でもちょっと頭打ちになってきてそう。

f:id:abrahamcow:20180228220402p:plain

社会・人口統計体系
都道府県データ / 社会生活統計指標より

一方で、平均在院日数(用語の説明)は減少傾向にあります。

f:id:abrahamcow:20180228220651p:plain

社会・人口統計体系
都道府県データ / 社会生活統計指標より

精神病床数も減少傾向です。

f:id:abrahamcow:20180228220754p:plain

社会・人口統計体系
都道府県データ / 社会生活統計指標より

入院期間ごとに近年の患者数をみても、減少傾向が伺えます。

f:id:abrahamcow:20180228221255p:plain

患者調査 / 平成26年患者調査/ 閲覧(報告書非掲載表)
患者調査 / 平成23年患者調査/ 閲覧(報告書非掲載表)
患者調査 / 平成20年患者調査/ 閲覧(報告書非掲載表)より

こういうデータの出典ってこの書き方で大丈夫ですか?

以下にRのコードを載せます。

library(estatapi)library(tidyverse)
myappId <-"ここには自分のアプリケーションIDを入れる"

dat1 <-estat_getStatsData(appId = myappId, statsDataId ="0000010209")
unique(dat1$`I 健康・医療`)
nyuin <-dplyr::filter(dat1,`I 健康・医療`=="#I04103_精神科病院年間新入院患者数(人口10万人当たり)",地域=="全国")%>% 
  mutate(year=as.integer(substr(調査年,1,4)))

theme_set(theme_bw(14,"Osaka")+
            theme(axis.text = element_text(colour="black")))

p_nyuin <-ggplot(nyuin,aes(x=year,y=value))+
  geom_line()+
  labs(y="人口10万人当たり",x="年")+
  ggtitle("精神科病院年間新入院患者数(全国)")

ggsave("~/Desktop/p_nyuin.png",p_nyuin)

zaiin <-dplyr::filter(dat1,`I 健康・医療`=="#I10205_精神科病院平均在院日数",地域=="全国")%>% 
  mutate(year=as.integer(substr(調査年,1,4)))

p_zaiin <-ggplot(zaiin,aes(x=year,y=value))+
  geom_line()+
  labs(y="人口10万人当たり",x="年")+
  ggtitle("精神科病院平均在院日数(全国)")

ggsave("~/Desktop/p_zaiin.png",p_zaiin)

byosho <-dplyr::filter(dat1,`I 健康・医療`=="#I0910205_精神病床数(人口10万人当たり)",地域=="全国")%>% 
  mutate(year=as.integer(substr(調査年,1,4)))

p_byosho <-ggplot(byosho,aes(x=year,y=value))+
  geom_line()+
  labs(y="人口10万人当たり",x="年")+
  ggtitle("精神病床数(全国)")

ggsave("~/Desktop/p_byosho.png",p_byosho)

datId <-c("0003128737","0003071461","0003027589")
tim <- seq(2008,by=3,length.out =3)
dat2 <- lapply(datId,function(x){estat_getStatsData(appId = myappId,statsDataId = x)})

df <-sapply(1:3,function(i){dplyr::filter(dat2[[i]],grepl("5 精神及び行動の障害",傷病分類_001),入院期間_002!="総数",入院期間_002!="6月以上(再掲)",精神疾患の有無_001 =="精神疾患(副傷病)あり")%>% 
    mutate(year=tim[i])})%>% 
  bind_rows()
lev <-c("0~14日","15~30日","1月~3月","3月~6月","6月~1年","1年~3年","3年~5年","5年以上","不詳")
df <-df %>% 
  mutate(term=factor(入院期間_002,levels = lev))

p1 <-ggplot(df,aes(x=term,y=value,fill=factor(year)))+
  geom_col(position ="dodge")+
  labs(fill="調査年",y="千人",x="入院期間")+
  facet_wrap(~傷病分類_001,scales ="free_y",nrow=4)
print(p1)
ggsave("~/Desktop/p1.png",p1,width =10)

[googleAnalyticsR]調整済みオッズ比の四半期比較プロット

$
0
0

とりあえず図とコードだけ貼ります。

f:id:abrahamcow:20180301011921p:plain

縦の棒はオッズ比の95%信頼区間です。

library(googleAnalyticsR)library(tidyverse)library(lubridate)#####
ga_auth()
account_list <- ga_account_list()
ga_id <- account_list$viewId[3]

gadata <-
  google_analytics_4(ga_id,
                     date_range = c("2015-12-01","2018-02-28"),
                     metrics = c("sessions","goal3Completions"),
                     dimensions = c("yearMonth","channelGrouping","userType"))

gadata2 <- gadata %>% 
  dplyr::filter(channelGrouping!="(Other)"&channelGrouping!="Email")%>% 
  mutate(channelGrouping=relevel(factor(channelGrouping),"Organic Search"))%>% 
  mutate(date=as.Date(paste0(yearMonth,"01"),format="20%y%m%d"))%>% 
  mutate(Q=round_date(date,"3 months")-months(1))%>% 
  group_by(Q,channelGrouping,userType)%>% 
  summarise(sessions=sum(sessions),CVs=sum(goal3Completions))%>% 
  ungroup()

u_Q <-unique(gadata2$Q)

fitlist <-lapply(u_Q,function(q){
  glm(cbind(CVs,sessions-CVs)~ channelGrouping + userType,
    data=dplyr::filter(gadata2,Q==q),family ="binomial")})

CIdf <-lapply(fitlist,function(x)data.frame(variables=names(x$coefficients),
                                             value=exp(x$coefficients),exp(confint(x))))%>% 
  bind_rows()
CIdf <- mutate(CIdf,Q=rep(u_Q,each=n_distinct(CIdf$variables)))

p1<-ggplot(CIdf,aes(x=Q,y=value,ymin=X2.5..,ymax=X97.5..))+
  geom_line()+
  geom_pointrange()+
  facet_wrap(~variables,scales ="free_y",nrow =5)+
  scale_x_date(breaks=u_Q)+
  theme_bw()+
  theme(axis.text = element_text(colour="black"))
print(p1)
ggsave(filename ="~/Desktop/p1.png",p1)

Rcpp: カーネル密度推定のバンド幅を一個抜き交差検証法で決める

$
0
0

バンド幅 h をいろいろ変えて一個抜き交差検証法で評価した対数尤度が結構なめらかな形になったのでブレント法で最適なバンド幅を選んでみた。

対象としたデータはこれ。

f:id:abrahamcow:20180320011441p:plain

h をいろいろ変えて一個抜き交差検証法で評価した対数尤度のプロットはこちら。

f:id:abrahamcow:20180320011532p:plain

選ばれた最適なバンド幅で推定した密度関数がこんな感じ。

f:id:abrahamcow:20180320011656p:plain

Rcpp のコード:

#include <Rcpp.h>usingnamespace Rcpp;

// [[Rcpp::export]]double kernel_logistic(double y, NumericVector x, double bw){
  return mean(1/(exp((y-x)/bw)+exp((x-y)/bw)+2))/bw;
}

Rcpp::NumericVector omit(NumericVector x, int i) {
  x.erase(i);
  return x;
}

// [[Rcpp::export]]double looll(double bw, NumericVector x) {
  int N = x.length();
  double ll=0;
  for(int i=0;i<N;i++){
    ll =ll+ log(kernel_logistic(x[i],omit(x,i),bw));
  }
  return ll;
}

R のコード:

library(Rcpp)
sourceCpp("kernel_logis.cpp")
x <- faithful$waiting
hist(x,breaks ="FD")
h <- seq(0,2,by=0.01)
sap<-sapply(h,function(h)looll(bw=h,x=x))
plot(h,sap,ylab ="log-likelihood")
opt_looll <-optim(0.2,looll,x=x,control=list(fnscale=-1),method ="Brent",
                  lower =0, upper=2)
kernel_logistic0 <-function(x,bw){function(y){sapply(y,function(y){kernel_logistic(y,x,bw)})}}
kernel_logistic_x <- kernel_logistic0(x,opt_looll$par)

hist(x,freq =FALSE,breaks ="FD")
curve(kernel_logistic_x(x),add =TRUE,col="royalblue",lwd=2)

参考:カーネル (統計学) - Wikipedia

目で見る尤度関数(『ベイズ統計の理論と方法』より)

$
0
0

ベイズ統計の理論と方法』1.4節の例を R でやってみます。

ベイズ統計の理論と方法

ベイズ統計の理論と方法

尤度関数が正規分布で近似できるとき、いろいろ良い性質がなりたちます。

関数 \phi(x)を標準正規分布の密度関数とし、確率モデル

 p(x)=(1-a) \phi(x) + a \phi(x-b)
(ただし  0 < a< 1) から100個の乱数を生成して、尤度関数の形を見ていきます。

真のパラメータが、(a_0,b_0)=(0.5,3)のとき、ヒストグラムを書いてみると、密度関数は混合分布で表せそうに見えます。

f:id:abrahamcow:20180330235444p:plain

最尤推定量は (0.52, 2.90) であり、真のパラメータの近くにあります。

尤度関数の等高線に多変量正規分布から生成した乱数を重ねてみると、尤度関数は正規分布で近似できそうに見えます。

f:id:abrahamcow:20180330235753p:plain

真のパラメータが、(a_0,b_0)=(0.5,1)のとき、ヒストグラムを書いてみると、ぱっと見では混合分布だとはわからないかもしれません。

f:id:abrahamcow:20180331000808p:plain

最尤推定量は (0.45 1.17) であり、まあまあ真のパラメータの近くにあります。

尤度関数の等高線はちょっといびつな形をしており、多変量正規分布で近似するのは少々強引な気がします。

f:id:abrahamcow:20180331001027p:plain

真のパラメータが、(a_0,b_0)=(0.5,0.5)のとき、ヒストグラムを書いてみると、やはりぱっと見では混合分布だとはわからないかもしれません。

f:id:abrahamcow:20180331001209p:plain

最尤推定量は (0.31 0.67) でした。真のパラメータからは少しずれています。

尤度関数の等高線は局所的にするどいピークを持ち、正規分布で近似するのはあきらかに無理があります。

f:id:abrahamcow:20180331001413p:plain

以下に R のコードを貼ります。

library(mvtnorm)library(tidyverse)
rmixnorm <-function(n,a,b){
  Y <-sample.int(2, n, replace=TRUE, prob=c(1-a,a))
  mu <- c(0, b)
  rnorm(n, mu)}
dmixnorm <-function(par,x,log=TRUE){
  a0 <- par[1]
  b0 <- par[2]
  ll <- sum(log((1-a0)*dnorm(x)+a0*dnorm(x,b0)))if(log){
    ll
  }else{
    exp(ll)}}
set.seed(1)
x1 <- rmixnorm(100,0.5,3.0)
x2 <- rmixnorm(100,0.5,1.0)
x3 <- rmixnorm(100,0.5,0.5)
hist(x1)
hist(x2)
hist(x3)
opt1 <- optim(c(0.5,1), dmixnorm,control =list(fnscale=-1), x=x1, method ="L-BFGS-B",
             lower=c(0,-5),upper=c(1,5),hessian =TRUE)
opt2 <- optim(c(0.5,1), dmixnorm,control =list(fnscale=-1), x=x2, method ="L-BFGS-B",
             lower=c(0,-5),upper=c(1,5),hessian =TRUE)
opt3 <- optim(c(0.5,1), dmixnorm,control =list(fnscale=-1), x=x3)
print(round(opt1$par,2))
print(round(opt2$par,2))
print(round(opt3$par,2))
a <- seq(0,1,length.out =200)
b <- seq(-5,5,length.out =200)
parms <-expand.grid(a,b)
L1 <- apply(parms,1,dmixnorm,x=x1,log=FALSE)
datL1 <- as_data_frame(parms)%>% 
  set_names(c("a","b"))%>% 
  mutate(L=L1)
sample_out1 <-as_data_frame(rmvnorm(10000,opt1$par,-solve(opt1$hessian)))%>% 
  set_names(c("a","b"))
ggplot(datL1,aes(x=a,y=b))+
  geom_point(data=sample_out1,alpha=0.2)+
  geom_contour(aes(z=L),size=1)+
  theme_bw()

L2 <- apply(parms,1,dmixnorm,x=x2,log=FALSE)
datL2 <- as_data_frame(parms)%>% 
  set_names(c("a","b"))%>% 
  mutate(L=L2)
sample_out2 <-as_data_frame(rmvnorm(10000,opt2$par,-solve(opt2$hessian)))%>% 
  set_names(c("a","b"))
ggplot(datL2,aes(x=a,y=b))+
  geom_point(data=sample_out2,alpha=0.2)+
  geom_contour(aes(z=L),size=1)+
  theme_bw()

L3 <- apply(parms,1,dmixnorm,x=x3,log=FALSE)
datL3 <- as_data_frame(parms)%>% 
  set_names(c("a","b"))%>% 
  mutate(L=L3)
ggplot(datL3,aes(x=a,y=b))+
  geom_contour(aes(z=L),size=1)+
  theme_bw()




目で見る尤度関数

TikzのBayesNetライブラリでプレート表現を書いてみる

$
0
0

プレート表現については、たしか『実践 ベイズモデリング』などに解説がある。

実践 ベイズモデリング -解析技法と認知モデル-

実践 ベイズモデリング -解析技法と認知モデル-

プレート表現に慣れたいので、TikzのBayesNetライブラリ(GitHub - jluttine/tikz-bayesnet: TikZ library for drawing Bayesian networks, graphical models and (directed) factor graphs in LaTeX.)を使って、過去にこのブログで書いたモデルで練習してみる。

[RStan]多項ロジスティックモデルで μ's とAqours の人気の差を調べる - 廿TTで使ったモデルはこんな感じ。

f:id:abrahamcow:20180404071730p:plain

灰色のノードが観測された変数、白が未観測の変数(というかパラメータ)を表す。
四角(プレート)は囲まれた変数が N 回生成されたことを示している。

\alphapも同じ白丸で表すことに違和感があるけど、密度関数がデルタ関数の分布から生成されたと思えばいい(のかな?)。

texのソースはこちら。

¥documentclass[dvipdfmx, border={2pt2pt2pt2pt}]{standalone}¥usepackage{tikz}¥usetikzlibrary{bayesnet}¥begin{document}¥centering
¥begin{tikzpicture}% Nodes¥node[latent](sigma){$¥sigma$}; 
¥node[latent, right = of sigma](b){$b_i$} ; 
¥node[obs, above = of b](x){$x_i$} ; 
¥node[latent, right = of b](alpha){$¥alpha_i$} ; 
¥node[latent, above = of x](g){$g$} ; 
¥node[latent, right = of alpha](p) {$p_i$} ;
¥node[obs, right = of p](y) {$y_i$} ; 

%edge¥edge {sigma}{b};
¥edge {b, g, x}{alpha};
¥edge {alpha}{p};
¥edge {p}{y};

% Plates¥plate {pl}{(b)(x)(alpha)(p)(y)}{$i=1¥dots N$} ;
¥end{tikzpicture}¥end{document}

つづいて、[RStan]項目反応理論の応用でフリースタイルダンジョン登場ラッパーの強さをランキングしてみた - 廿TTで使ったモデルはこんな感じ。

f:id:abrahamcow:20180404072220p:plain

texのソースはこちら。

¥documentclass[dvipdfmx, border={2pt2pt2pt2pt}]{standalone}¥usepackage{tikz, amsmath}¥usetikzlibrary{bayesnet}¥begin{document}¥centering
¥begin{tikzpicture}% Nodes¥node[latent](c){$¥boldsymbol{c}$}; 
¥node[latent, right = of c](eta){$¥eta$} ; 
¥node[obs, below = of eta](score){${¥rm score}$} ;  

%edge¥edge {eta}{score};
¥edge {c}{score};

% Plates¥plate {pl1}{(eta)}{$m$} ;
 ¥plate {pl2}{(score)}{$n$} ;
¥end{tikzpicture}¥end{document}

[改訂第7版]LaTeX2ε美文書作成入門

[改訂第7版]LaTeX2ε美文書作成入門

ggplot2 でミニ棒グラフ行列を描画する geom_barmatrix を書きました

$
0
0
library(tidyverse)
geom_barmatrix <-function(mapping =NULL, data =NULL,
                           stat ="identity",position ="identity",
                           ...,
                           width =NULL,
                           binwidth =NULL,
                           na.rm =FALSE,
                           show.legend =NA,
                           inherit.aes =TRUE){
  
  layer(
    data = data,
    mapping = mapping,
    stat = stat,
    geom = GeomBarmatrix,
    position = position,
    show.legend = show.legend,
    inherit.aes = inherit.aes,
    params =list(
      width = width,
      na.rm = na.rm,
      ...
    ))}

GeomBarmatrix <- ggproto("GeomBarmatrix", GeomRect,
                         required_aes = c("x","y","inner_y"),
                         
                         setup_data =function(data, params){
                           data$width <- data$width %||%
                             params$width %||%(resolution(data$x,FALSE)*0.9)
                           transform(data,
                                     ymin = y, ymax = y+0.9*inner_y/max(inner_y)*min(diff(unique(sort(y)))),
                                     xmin = x - width /2, xmax = x + width /2, width =NULL)},
                         
                         draw_panel =function(data, panel_params, coord, width =NULL){
                           GeomRect$draw_panel(data, panel_params, coord)})

geom_barmatrixframe <-function(mapping =NULL, data =NULL,
                                stat ="identity",position ="identity",
                                ...,
                                width =NULL,
                                binwidth =NULL,
                                na.rm =FALSE,
                                show.legend =NA,
                                inherit.aes =TRUE){
  
  layer(
    data = data,
    mapping = mapping,
    stat = stat,
    geom = GeomBarmatrixframe,
    position = position,
    show.legend = show.legend,
    inherit.aes = inherit.aes,
    params =list(
      width = width,
      na.rm = na.rm,
      ...
    ))}

GeomBarmatrixframe <- ggproto("GeomBarmatrixframe", GeomRect,
                              required_aes = c("x","y"),
                              default_aes = aes(colour ="Black", fill =NA, size =0.5, linetype =1,
                                                alpha =NA),
                              
                              setup_data =function(data, params){
                                data$width <- data$width %||%
                                  params$width %||%(resolution(data$x,FALSE)*0.9)
                                transform(data,
                                          ymin = y, ymax = y+0.9*min(diff(unique(sort(y)))),
                                          xmin = x - width /2, xmax = x + width /2, 
                                          width =NULL)},
                              
                              draw_panel =function(data, panel_params, coord, width =NULL){
                                GeomRect$draw_panel(data, panel_params, coord)})

以下、デモです。

総務省労働力調査「第12表 年齢階級別就業者数」と「第13表 年齢階級別完全失業者数」を使います。

統計局ホームページ/労働力調査(基本集計) 平成30年(2018年)4月分結果

そのままの形式だと扱いにくかったので加工したファイルを置いておきます。

https://gist.github.com/abikoushi/f565a3bdce8490a82cee22de6577217f

年代、性別、年ごとの就業者数のグラフです。太い棒の高さが就業者数です。

f:id:abrahamcow:20180616073434p:plain

shitsugyo_and_shugyo_shasu <-read.csv("shitsugyo_and_shugyo_shasu.csv")
ggplot(shitsugyo_and_shugyo_shasu,aes(x=age,y=year,inner_y=shugyo))+
  geom_barmatrix()+
  facet_wrap(~sex)+
  theme_bw()

枠がついていたほうが見やすいかなと思って geom_barmatrixframe も書きました。

geom_barmatrixframe を足してやるとこうなります。

f:id:abrahamcow:20180616073759p:plain

(なんかコップに黒い液体が満たされているように見える)

ggplot(shitsugyo_and_shugyo_shasu,aes(x=age,y=year,inner_y=shugyo))+
  geom_barmatrixframe()+
  geom_barmatrix()+
  facet_wrap(~sex)+
  theme_bw(base_family ="Osaka")

棒の色で失業率も表すと4次元棒グラフが出来上がります。

f:id:abrahamcow:20180616073948p:plain

ggplot(shitsugyo_and_shugyo_shasu,aes(x=age,y=year,inner_y=shugyo))+
  geom_barmatrix(aes(fill=shitsugyo/(shugyo+shitsugyo)))+
  geom_barmatrixframe()+
  scale_fill_continuous(labels=scales::percent)+
  facet_wrap(~sex)+
  labs(fill="失業率")+
  theme_bw(base_family="Osaka")

枠とっちゃって x 軸を連続量っぽく見せる手もあります。

f:id:abrahamcow:20180616165617p:plain

ggplot(shitsugyo_and_shugyo_shasu,aes(x=age,y=year,inner_y=shugyo))+
  geom_barmatrix(aes(fill=shitsugyo/(shugyo+shitsugyo)),width =1)+
  scale_fill_continuous(labels=scales::percent)+
  facet_wrap(~sex)+
  labs(fill="失業率")+
  theme_bw(base_family="Osaka")
  • 積み上げ棒グラフにも対応したい
  • 内側にも小さい y 軸をつけたい

けどどうやればいいかわからない。教えていただけると嬉しいです。

ggplot2 のためのいくつかの関数をパッケージ化しました

$
0
0

まだ説明とかぜんぜん書いてない。

GitHub - abikoushi/ggsomestat: Some stat in ggplot2

インストールは

devtools::install_github("abikoushi/ggsomestat")

で、たぶんいけます。

練習用にsearchConsoleというsearchConsoleからとってきたデータが入ってます。

stat_ma は移動平均を描画する stat です。

f:id:abrahamcow:20180618101601p:plain

library(ggplot2)library(dplyr)library(ggsomestat)

entry0117 <-searchConsole %>% 
  dplyr::filter(pagePath=="/entry/2015/01/17/064522")

ggplot(entry0117, aes(date, clicks))+ 
  geom_point()+
  stat_ma(windowsize =14)

移動平均の期間の幅は windowsize という引数で変更できます。
(デフォルトは7)

stat_binomCI は二項分布のパラメータの信頼区間を描画します。

f:id:abrahamcow:20180618102017p:plain

ggplot(entry0117, aes(date, clicks/impressions))+ 
  geom_point()+
  stat_binomCI(aes(numerator=clicks,denominator=impressions), conf.level =0.8)

信頼水準は conf.level という引数で変更できます。
(デフォルトは0.95)

linerange が見づらいと思ったら geom を適宜変えてください。

f:id:abrahamcow:20180618102202p:plain

ggplot(entry0117, aes(date, clicks/impressions))+ 
  geom_line()+
  stat_binomCI(geom ="ribbon",aes(numerator=clicks,denominator=impressions),alpha=0.3)

エラーバーつき棒グラフ。

f:id:abrahamcow:20180618102333p:plain

total <-searchConsole %>% 
  group_by(pagePath)%>% 
  summarise(clicks=sum(clicks),impressions=sum(impressions))

ggplot(total,aes(x=pagePath,y=clicks/impressions))+
  geom_col(alpha=0.5,colour="black")+
  stat_binomCI(aes(numerator=clicks,denominator=impressions),
               geom ="errorbar",
               width=0.5)+
  coord_flip()

theme_sparkline はスパークライン(ミニ折れ線グラフ)を書くのに適したテーマです。

f:id:abrahamcow:20180618102426p:plain

ggplot(searchConsole, aes(date, clicks))+ 
  geom_line()+
  facet_grid(pagePath~.)+
  theme_sparkline()

stat_ma と組み合わせるとこんな感じ。

f:id:abrahamcow:20180618102520p:plain

ggplot(searchConsole, aes(date, clicks))+ 
  geom_line()+
  stat_ma(colour="royalblue")+
  facet_grid(pagePath~.)+
  theme_sparkline()

もちろん geom_line 以外に使っても構いません。

f:id:abrahamcow:20180618102901p:plain

ggplot(searchConsole, aes(date, clicks))+ 
  geom_col(aes(fill=clicks/impressions))+
  facet_grid(pagePath~.)+
  theme_sparkline()

参考にしたページ:Rの自分用関数をGithub的公開型パッケージにする - ryamadaのコンピュータ・数学メモ

git も githubもぜんぜんわかってないので変なことやってたらすみません。

以上です。

打ち切りデータを扱うために前に書いた R の関数群をパッケージ化しました

$
0
0

R の自分用の関数がごちゃごちゃしてきたのでパッケージ化して整理しようとしているのです。

tidysurv とかいう恐れ多い名前をつけてしまった……。

GitHub - abikoushi/tidysurv: R package for censored data handling

まだ作りかけです。dplyr、ggplot2 と survival パッケージに依存します。

インストールは

devtools::install_github("abikoushi/tidysurv")

です。動かなかったらすみません。ちゃんとテストしてないです。

tidysurv は survfit オブジェクトを tidy っぽいデータフレームにする関数です。

(本当はあまり tidy ではない:整然データとは何か|Colorless Green Ideas

require(tidysurv)require(tidyverse)require(survival)
lung2 <- lung %>% 
  filter(ph.ecog!=3)
sf <-survfit(Surv(time,status)~sex+ph.ecog,data=lung2)

df1 <-tidysurv(sf)

ggplot(df1,aes(x=time,y=survival,ymin=lower,ymax=upper,group=strata))+
  geom_step()+
  geom_ribbon(alpha=0.5)+
  facet_wrap(~strata)

survfit したやつをプロットしたりするのに使います。

f:id:abrahamcow:20180620225133p:plain


make_qdf は survfit オブジェクトのクォンタイルを取り出して tidy っぽいデータフレームにする関数です。

デフォルトだと25%点(25%の人が死亡した時点)、50%点、75%点を出力します。

df2 <-make_qdf(sf)

ggplot(df2,aes(x=strata,y=q50,ymin=q25,ymax=q75))+
  geom_pointrange()+
  coord_flip()

これをそのままプロットすると75%の人が観測終了時点でまだ死亡してないグループは消えてしまいます。

f:id:abrahamcow:20180620225419p:plain


そこで geom_cd_pointrange は NA をバツ印に置き換えて描画します(ただそれだけの関数です)。

ggplot(df2,aes(x=strata,y=q50,ymin=q25,ymax=q75))+
  geom_cd_pointrange()+
  coord_flip()

f:id:abrahamcow:20180620225709p:plain

make_hdfは打ち切りデータのヒストグラムRによる打ち切りデータのヒストグラム (ggplot2) - 廿TT)を作るための関数です。

bw <-120
df3 <- make_hdf(sf,bw)
ggplot(df3)+
  geom_col(aes(x=midtime,y=density),width = bw,fill="white",colour="black")+
  facet_wrap(~strata)

f:id:abrahamcow:20180620225846p:plain

はい。

完成未定です。

アマゾンアフィリエイト貼りたいけどなにがいいのかな。おすすめの本とかあったら教えてください。

RユーザのためのRStudio[実践]入門−tidyverseによるモダンな分析フローの世界−

RユーザのためのRStudio[実践]入門−tidyverseによるモダンな分析フローの世界−


ggplot2とpatchworkでヒートマップの周辺度数をプロット

$
0
0

パッチワークパッケージ(GitHub - thomasp85/patchwork: The Composer of ggplots)は便利ですね。

f:id:abrahamcow:20181108211410p:plain

library(tidyverse)library(patchwork)

data("author",package ="ca")
author_t <- as_data_frame(author)%>% 
  mutate(title=rownames(author))%>% 
  gather(alphabet,count,-title)

alphabet_t <- group_by(author_t,alphabet)%>% 
  summarise(count=sum(count))

p1<-ggplot(author_t,aes(x=title,y=alphabet,fill=count))+
  geom_tile()+
  scale_fill_continuous(low="white",high="black")+
  theme(legend.position ="left",axis.text.x = element_text(angle=90),
        plot.margin= unit(c(1,0,1,1),"lines"))

p2 <-ggplot(alphabet_t,aes(x=alphabet,y=count))+
  geom_col(fill="white",colour="black")+
  coord_flip()+
  theme(axis.text.y = element_blank(),axis.ticks.y = element_blank(),
        plot.margin= unit(c(1,1,1,-1),"lines"))+
  xlab("")(p1|p2)

参考:ggplot2とpatchworkで周辺分布 | Atusy's blog

ggplot2 で内訳を左から右に詳細化していく棒グラフ (2)

$
0
0

以前にこんなのを書いたんだが、
ggplot2 で内訳を左から右に詳細化していく棒グラフ - 廿TT
新しいgeomを作るまでもないことに気がついた。

library(tidyverse)
make_drilldown <-function(data,dimensions,metrics){
  len <- length(dimensions)
  out <-vector("list",len)
  dat1 <- as.data.frame(dplyr::summarize_each_(dplyr::group_by_(data, dimensions[1]),dplyr::funs(sum),metrics))
  dat1$level <- dat1[,1]
  dat1 <- setNames(dat1,c("level",metrics,"label"))
  dat1 <- dat1[order(dat1$label, decreasing =TRUE),]
  dat1$dimensions <- dimensions[1]
  out[[1]]<-dat1
  if(len>=2){for(i in2:len){
      tmp <- as.data.frame(dplyr::summarize_each_(dplyr::group_by_(data, .dots=dimensions[1:i]),dplyr::funs(sum),metrics))
      tmp <- tidyr::unite(tmp,level,1:i,remove=FALSE,sep="__")
      tmp <- tmp[,-c(2:i)]
      tmp <- setNames(tmp,c("level","label",metrics)) 
      tmp <- tmp[order(tmp$level,decreasing =TRUE),]
      tmp$dimensions <- dimensions[i]
      out[[i]]<- tmp
    }}
  out_df <- dplyr::bind_rows(out)
  out_df$dimensions <- factor(out_df$dimensions,levels = dimensions)
  out_df
}

Titanic_df <- make_drilldown(as.data.frame(Titanic),dimensions = c("Survived","Age","Sex","Class"),metrics ="Freq")

ggplot(Titanic_df,aes(x=dimensions,y=Freq,group=level,label=label,fill=label))+
  geom_col(colour="black",alpha=0.5,width=1)+
  geom_text(position = position_stack(vjust =0.5))+
  theme(legend.position ="none",axis.text.x = element_text(size=18))

f:id:abrahamcow:20190207030822p:plain

ggplot2で検索クエリのワードクラウド

$
0
0

ただパッケージ動かしてみたってだけなんですけど。

f:id:abrahamcow:20190209063403p:plain

はい。

ぼくは原則的にはワードクラウドを使う必要ない(棒グラフのほうが優れている)と思っているんですけど、最近軟弱になってきてる。

library(searchConsoleR)library(ggwordcloud)
scr_auth()
sc_websites <- list_websites()
scdata <- search_analytics(sc_websites[1,1],
                           startDate ="2019-01-01",
                           endDate ="2019-01-31",
                           dimensions = c("query"),
                           dimensionFilterExp ="page~~/entry/2017/05/05/150436")

ggplot(scdata,aes(label=query,size=impressions,colour=ctr))+
  geom_text_wordcloud(family="Osaka",show.legend =TRUE)+
  scale_size_area(max_size =16)+
  theme_minimal()

ggwordcloud: a word cloud geom for ggplot2

殺人事件の最近の傾向(平成19年から28年)

$
0
0

「殺人事件が年々増えているというのはメディアが作った嘘で実際は減少している」みたいな言説を最近よく聞く気がします。

ぼくはテレビ見ないし新聞も読まないので(見たほうがいいのはわかってるけど)メディアがどういう報道をしてるのかは知らない。

そこで、警察庁Webサイト(http://www.npa.go.jp/toukei/seianki/h28zuhyousakuin.htm)より最近の殺人事件の被害者数の推移をグラフにしてみました。

エクセル、久しぶりに使ったけど色使いの雰囲気がいつの間にかよくなった気がします。
(とはいえ、統計表をエクセルで公開するのはなんでなんだろう。csvじゃだめなの?)

f:id:abrahamcow:20190213232612p:plain
(「1-2-1-1-1 殺人認知・検挙状況、被害者数H19-H28の推移......... h28.1-2-1-1-1.xlsx」より)

ここ10年は減少傾向ですね。

ついでに年齢層別検挙人数です。

f:id:abrahamcow:20190213232812p:plain

f:id:abrahamcow:20190213232915p:plain

(どちらも「1-2-1-1-2 年齢層別殺人検挙人員、人口10万人当たり年齢層別殺人検挙人員H19-H28の推移.................................................... h28.1-2-1-1-2.xlsx」より)

年をとるとあまり人を殺さなくなるみたいですね。

[ggplot2]ヒストグラムを箱ひげ図風に並べるプロット

$
0
0

こんな感じです。

ggplot(data = iris)+
  geom_grid(aes(y = Sepal.Length, x=Species),binwidth =0.1)

f:id:abrahamcow:20190324062631p:plain

思い通りに動かないことも多いけど公開します。

皆様の暖かいアドバイスをお待ちしております。

  • coord_flip に対応したい
  • 四角が大きいとき隣の四角とかぶってしまうのをなんとかしたい
  • bins でビンの数を変えられるようにしたい
  • stack に対応したい
library(tidyverse)library(grid)
geom_grid <-function(mapping =NULL, data =NULL,
                         position ="identity",
                         ...,# bins = NULL,
                         binwidth =NULL,
                         binaxis ="y",
                         method ="dotdensity",
                         binpositions ="bygroup",
                         stackdir ="up",
                         stackratio =1,
                         dotsize =1,
                         stackgroups =FALSE,
                         origin =NULL,
                         right =TRUE,
                         width =0.9,
                         drop =FALSE,
                         na.rm =FALSE,
                         show.legend =NA,
                         inherit.aes =TRUE){# If identical(position, "stack") or position is position_stack(), tell them# to use stackgroups=TRUE instead. Need to use identical() instead of ==,# because == will fail if object is position_stack() or position_dodge()if(!is.null(position)&&(identical(position,"stack")||(inherits(position,"PositionStack"))))
    message("position=\"stack\" doesn't work properly with geom_dotplot. Use stackgroups=TRUE instead.")if(stackgroups && method =="dotdensity"&& binpositions =="bygroup")
    message('geom_dotplot called with stackgroups=TRUE and method="dotdensity". You probably want to set binpositions="all"')
  
  layer(
    data = data,
    mapping = mapping,
    stat = StatBindot,
    geom = GeomGrid,
    position = position,
    show.legend = show.legend,
    inherit.aes = inherit.aes,# Need to make sure that the binaxis goes to both the stat and the geom
    params =list(
      binaxis = binaxis,# bins = bins,
      binwidth = binwidth,
      binpositions = binpositions,
      method = method,
      origin = origin,
      right = right,
      width = width,
      drop = drop,
      stackdir = stackdir,
      stackratio = stackratio,
      dotsize = dotsize,
      stackgroups = stackgroups,
      na.rm = na.rm,
      ...
    ))}

GeomGrid <- ggproto("GeomGrid", Geom,
                       required_aes = c("x","y"),
                       non_missing_aes = c("size","shape"),
                       
                       default_aes = aes(colour ="black", fill ="white", alpha =NA, stroke =1, linetype ="solid"),
                       
                       setup_data =function(data, params){
                         data$width <- data$width %||%
                           params$width %||%(resolution(data$x,FALSE)*0.9)# Set up the stacking function and rangeif(is.null(params$stackdir)|| params$stackdir =="up"){
                           stackdots <-function(a)  a -.5
                           stackaxismin <-0
                           stackaxismax <-1}elseif(params$stackdir =="down"){
                           stackdots <-function(a)-a +.5
                           stackaxismin <--1
                           stackaxismax <-0}elseif(params$stackdir =="center"){
                           stackdots <-function(a)  a -1- max(a -1)/2
                           stackaxismin <--.5
                           stackaxismax <-.5}elseif(params$stackdir =="centerwhole"){
                           stackdots <-function(a)  a -1- floor(max(a -1)/2)
                           stackaxismin <--.5
                           stackaxismax <-.5}# Fill the bins: at a given x (or y), if count=3, make 3 entries at that x
                         data <- data[rep(1:nrow(data), data$count),]# Next part will set the position of each dot within each stack# If stackgroups=TRUE, split only on x (or y) and panel; if not stacking, also split by group# plyvars <- params$binaxis %||% "x"# plyvars <- params$binaxis %||% "x"# plyvars <- c(plyvars, "PANEL")# if (is.null(params$stackgroups) || !params$stackgroups)#   plyvars <- c(plyvars, "group")# Within each x, or x+group, set countidx=1,2,3, and set stackpos according to stack function# data <- dapply(data, plyvars, function(xx) {#   xx$countidx <- 1:nrow(xx)#   xx$stackpos <- stackdots(xx$countidx)#   xx# })
                         data <-  data %>%
                           dplyr::group_by_("x","y","PANEL","group")%>%
                           dplyr::mutate(countidx = row_number())%>%
                           dplyr::mutate(stackpos = stackdots(countidx))# Set the bounding boxes for the dotsif(is.null(params$binaxis)|| params$binaxis =="x"){# ymin, ymax, xmin, and xmax define the bounding rectangle for each stack# Can't do bounding box per dot, because y position isn't real.# After position code is rewritten, each dot should have its own bounding box.
                           data$xmin <- data$x - data$binwidth /2
                           data$xmax <- data$x + data$binwidth /2
                           data$ymin <- stackaxismin
                           data$ymax <- stackaxismax
                           data$y    <-0}elseif(params$binaxis =="y"){# ymin, ymax, xmin, and xmax define the bounding rectangle for each stack# Can't do bounding box per dot, because x position isn't real.# xmin and xmax aren't really the x bounds, because of the odd way the grob# works. They're just set to the standard x +- width/2 so that dot clusters# can be dodged like other geoms.# After position code is rewritten, each dot should have its own bounding box.
                           data <- dplyr::mutate(dplyr::group_by(data, group, PANEL),
                                          ymin = min(y)- binwidth /2,
                                          ymax = max(y)+ binwidth /2)
                           
                           data$xmin <- data$x + data$width * stackaxismin
                           data$xmax <- data$x + data$width * stackaxismax
                           # Unlike with y above, don't change x because it will cause problems with dodging}
                         data
                       },
                       
                       
                       draw_group =function(data, panel_params, coord, na.rm =FALSE,
                                             binaxis ="x", stackdir ="up", stackratio =1,
                                             dotsize =1, stackgroups =FALSE){if(!coord$is_linear()){
                           warning("geom_grid does not work properly with non-linear coordinates.")}
                         
                         tdata <- coord$transform(data, panel_params)# Swap axes if using coord_flipif(inherits(coord,"CoordFlip"))
                           binaxis <- ifelse(binaxis =="x","y","x")if(binaxis =="x"){
                           stackaxis ="y"
                           dotdianpc <- dotsize * tdata$binwidth[1]/(max(panel_params$x.range)- min(panel_params$x.range))}elseif(binaxis =="y"){
                           stackaxis ="x"
                           dotdianpc <- dotsize * tdata$binwidth[1]/(max(panel_params$y.range)- min(panel_params$y.range))}
                         
                         ggplot2:::ggname("geom_grid",
                                rectstackGrob(stackaxis = stackaxis, x = tdata$x, y = tdata$y, dotdia = dotdianpc,
                                             stackposition = tdata$stackpos, stackratio = stackratio,
                                             default.units ="npc",
                                             gp = gpar(col = alpha(tdata$colour, tdata$alpha),
                                                       fill = alpha(tdata$fill, tdata$alpha),
                                                       lwd = tdata$stroke, lty = tdata$linetype)))},
                       
                       draw_key = draw_key_dotplot
)

rectstackGrob <-function(
  x = unit(0.5,"npc"),# x pos of the dotstack's origin
  y = unit(0.5,"npc"),# y pos of the dotstack's origin
  stackaxis ="y",
  dotdia = unit(1,"npc"),# Dot diameter in the non-stack axis, should be in npc
  stackposition =0,# Position of each dot in the stack, relative to origin
  stackratio =1,# Stacking height of dots (.75 means 25% dot overlap)
  default.units ="npc", name =NULL, gp = gpar(), vp =NULL){if(!grid::is.unit(x))
    x <- unit(x, default.units)if(!grid::is.unit(y))
    y <- unit(y, default.units)if(!grid::is.unit(dotdia))
    dotdia <- unit(dotdia, default.units)if(attr(dotdia,"unit")!="npc")
    warning("Unit type of dotdia should be 'npc'")
  
  grob(x = x, y = y, stackaxis = stackaxis, dotdia = dotdia,
       stackposition = stackposition, stackratio = stackratio,
       name = name, gp = gp, vp = vp, cl ="rectstackGrob")}


makeContext.rectstackGrob <-function(x, recording =TRUE){# Need absolute coordinates because when using npc coords with circleGrob,# the radius is in the _smaller_ of the two axes. We need the radius# to instead be defined in terms of the non-stack axis.
  xmm <- convertX(x$x,"mm", valueOnly =TRUE)
  ymm <- convertY(x$y,"mm", valueOnly =TRUE)if(x$stackaxis =="x"){
    dotdiamm <- convertY(x$dotdia,"mm", valueOnly =TRUE)
    xpos <- xmm + dotdiamm *(x$stackposition * x$stackratio +(1- x$stackratio)/2)
    ypos <- ymm
  }elseif(x$stackaxis =="y"){
    dotdiamm <- convertX(x$dotdia,"mm", valueOnly =TRUE)
    xpos <- xmm
    ypos <- ymm + dotdiamm *(x$stackposition * x$stackratio +(1- x$stackratio)/2)}
  
  grid::rectGrob(
    x = xpos, y = ypos, width = dotdiamm , height = dotdiamm ,
    default.units ="mm",
    name = x$name, gp = x$gp, vp = x$vp
  )}


ggplot(data = iris)+
  geom_grid(aes(y = Sepal.Length, x=Species),binwidth =0.1)

[ggplot2]ヒストグラムを箱ひげ図風に並べるプロット(2)

$
0
0

[ggplot2]ヒストグラムを箱ひげ図風に並べるプロット - 廿TTをちょっと修正しました。

こんな感じです。

source("https://gist.githubusercontent.com/abikoushi/d7e443774155de3934b754cf1a66625a/raw/74d451393ec9e1a6537122d2c39d5ee2d7559965/geom_grid.R")

ggplot(data = iris)+
  geom_grid(aes(y = Sepal.Length, x=Species), bins=20)

ggplot(data = mpg,aes(y = cty, x=factor(year), fill=factor(cyl)))+
  geom_grid(binwidth=1)

f:id:abrahamcow:20190325061320p:plain

f:id:abrahamcow:20190325061332p:plain

あとは四角が大きいとき隣の四角とかぶってしまうのをなんとかしたい。

引き続きアドバイスを期待しております。

[ggplot2]ヒストグラムを箱ひげ図風に並べるプロット(3)

$
0
0

追記:パッケージ化したのでこちらを参照してください→ggplot2でヒストグラムを箱ひげ図風に並べてプロットするパッケージggbrickを書いた - 廿TT


進捗です。

こんな感じです。

source("https://gist.githubusercontent.com/abikoushi/c7220524d44663e2694a967584207eb3/raw/76de298dac5d1353804a6213e68dbe61af599619/geom_grid.R")

ggplot(data = iris)+
  geom_grid(aes(y = Sepal.Length, x=Species), binwidth =0.1)

f:id:abrahamcow:20190325130727p:plain

ggplot(data = mpg,aes(y = cty, x=factor(year), fill=factor(cyl)))+
  geom_grid(binwidth =1)

f:id:abrahamcow:20190325130755p:plain

ggplot(data = diamonds, aes(x = color, y=carat, colour=cut))+
  geom_grid(binwidth=0.2)+
  coord_flip()

f:id:abrahamcow:20190325130821p:plain

とりあえずやりたいことはできそう。

たぶんまだ不具合とかあると思うので、落ち着いたらパッケージにしてギットハブにあげようと思います。


ggplot2でヒストグラムを箱ひげ図風に並べてプロットするパッケージggbrickを書いた

$
0
0

github.com

ggtetrisって名前にしようかと思ったんだけど、もうある(GitHub - EmilHvitfeldt/ggtetris: Create Tetris Chart Visualizations in R)みたいなので、ggbrickにしました。

brickはレンガっていう意味らしいです。

マニュアルはまだ一文字も書いてません。

インストールは

devtools::install_github("abikoushi/ggbrick")

で多分いけます。

入ってる関数は基本的にはgeom_brickだけです。

気が向いたら触ってみて変なところを教えていただけると嬉しいです。

(マニュアルも要望があれば書きます。)

以下デモです。

基本的な使い方はこう。

library(ggplot2)library(ggbrick)
ggplot(data = iris)+
  geom_brick(aes(y = Sepal.Length, x=Species), binwidth =0.1)

f:id:abrahamcow:20190325205328p:plain

引数binwidthまたはbinsでビンの幅を変えられます。

ggplot(data = iris)+
  geom_brick(aes(y = Sepal.Length, x=Species), binwidth =0.5)

f:id:abrahamcow:20190325205431p:plain

塗りつぶし。

ggplot(data = iris)+
  geom_brick(aes(y = Sepal.Length, x=Species), binwidth =0.5, fill ="black")

f:id:abrahamcow:20190325205501p:plain

色を変えてサブカテゴリを積み上げることができます。

ggplot(data = mpg,aes(y = cty, x=factor(year), fill=factor(cyl)))+
  geom_brick(binwidth =1)

f:id:abrahamcow:20190325205552p:plain

引数stackgroupsをFALSEにすると積み上げません。

ggplot(data = mpg,aes(y = cty, x=factor(year), fill=factor(cyl)))+
  geom_brick(binwidth =1, stackgroups =FALSE, alpha =0.5)

f:id:abrahamcow:20190325205637p:plain

でも見づらいのであんまり使わないかも。

stackdirを"centerwhole"にするとバイオリンプロットライクに左右にブロックを積みます。

ggplot(data = mpg,aes(y = cty, x=factor(year), fill=factor(cyl)))+
  geom_brick(binwidth =1, stackgroups =FALSE, alpha =0.5,
            stackdir ="centerwhole", position = position_dodge(0.5))

f:id:abrahamcow:20190325205748p:plain

横向きにしたいときはcoord_flipで。

ggplot(data = diamonds, aes(x = color, y=carat, colour=cut))+
  geom_brick(binwidth=0.2)+
  coord_flip()

f:id:abrahamcow:20190325205839p:plain

stat_summaryと合わせてメディアンを表示。

ggplot(data = iris,aes(y = Sepal.Length, x=Species))+
  geom_brick(binwidth =0.1, stackdir ="centerwhole")+
  stat_summary(fun.y = median, fun.ymin = median, fun.ymax = median,
               geom ="crossbar")

f:id:abrahamcow:20190325205942p:plain

当たり前かもしれませんがfacetもできます。

iris2 <- tidyr::gather(iris,key,value,-Species)
ggplot(data = iris2,aes(y = value, x=Species))+
  geom_brick(binwidth =0.3,fill="black")+
  facet_wrap(~key,scales ="free_y")

f:id:abrahamcow:20190325210030p:plain

以上です。

ggbrick: histogram like dot plot based on ggplot2

$
0
0

Hello everyone. I introduce an R package ggbrick.

github.com

My English is poor. If you don't understand my writing, please use comment field (コメントを書く).

ggbrick provides the function geom_brick which is a fun alternative to geom_violin or geom_boxplot.

Install

devtools::install_github("abikoushi/ggbrick")

Example

library(ggplot2)library(ggbrick)
ggplot(data = iris)+
  geom_brick(aes(y = Sepal.Length, x=Species), binwidth =0.1)

f:id:abrahamcow:20190325205328p:plain

The argument binwidth or bins make change bins width.

ggplot(data = iris)+
  geom_brick(aes(y = Sepal.Length, x=Species), binwidth =0.5)

f:id:abrahamcow:20190325205431p:plain

fill.

ggplot(data = iris)+
  geom_brick(aes(y = Sepal.Length, x=Species), binwidth =0.5, fill ="black")

f:id:abrahamcow:20190325205501p:plain

You can change the color and stack the rectangles.

ggplot(data = mpg,aes(y = cty, x=factor(year), fill=factor(cyl)))+
  geom_brick(binwidth =1)

f:id:abrahamcow:20190325205552p:plain

If stackgroups = FALSE:

ggplot(data = mpg,aes(y = cty, x=factor(year), fill=factor(cyl)))+
  geom_brick(binwidth =1, stackgroups =FALSE, alpha =0.5)

f:id:abrahamcow:20190325205637p:plain

If stackdir = "centerwhole":

ggplot(data = mpg,aes(y = cty, x=factor(year), fill=factor(cyl)))+
  geom_brick(binwidth =1, stackgroups =FALSE, alpha =0.5,
            stackdir ="centerwhole", position = position_dodge(0.5))

f:id:abrahamcow:20190325205748p:plain

When you want to turn sideways, use coord_flip:

ggplot(data = diamonds, aes(x = color, y=carat, colour=cut))+
  geom_brick(binwidth=0.2)+
  coord_flip()

f:id:abrahamcow:20190325205839p:plain

You can add stat_summary:

ggplot(data = iris,aes(y = Sepal.Length, x=Species))+
  geom_brick(binwidth =0.1, stackdir ="centerwhole")+
  stat_summary(fun.y = median, fun.ymin = median, fun.ymax = median,
               geom ="crossbar")

f:id:abrahamcow:20190325205942p:plain

You can use facet:

iris2 <- tidyr::gather(iris,key,value,-Species)
ggplot(data = iris2,aes(y = value, x=Species))+
  geom_brick(binwidth =0.3,fill="black")+
  facet_wrap(~key,scales ="free_y")

f:id:abrahamcow:20190325210030p:plain

Anscombe's quartet

I'd like to plot the data set available from the following page in several ways.

The Datasaurus Dozen - Same Stats, Different Graphs: Generating Datasets with Varied Appearance and Identical Statistics through Simulated Annealing | Autodesk Research

geom_jitter:

f:id:abrahamcow:20190326024556p:plain

It is a visualization which is faithful to the data. However, when the data points increases, it is difficult to show the frequency.

geom_boxplot:

f:id:abrahamcow:20190326024702p:plain

The boxplot only shows summarized statistics. In this data set, you can not see any difference in the distributions.

geom_brick:

f:id:abrahamcow:20190326024903p:plain

I think that the distribution can be understood.

geom_violin:

f:id:abrahamcow:20190326025019p:plain

pretty good, but the violinplots sometimes make over smoothing.

R code is here:

library(tidyverse)library(ggbrick)
dat <- read_tsv("~/Downloads/SameStatsDataAndImages/datasets/BoxPlots.tsv") 
dat_t <- gather(dat,key,value,-X1)

ggplot(dat_t,aes(x=key,y=value))+
  geom_jitter()

ggplot(dat_t,aes(x=key,y=value))+
  geom_boxplot()

ggplot(dat_t,aes(x=key,y=value))+
  geom_brick()

ggplot(dat_t,aes(x=key,y=value))+
  geom_violin()

腸内細菌叢のデータで遊ぼう(Kostic et al, 2015)

$
0
0

Kostic et al, 2015 (https://www.cell.com/cell-host-microbe/fulltext/S1931-3128(15)00021-9) では、乳幼児の腸内細菌叢のコホート研究をおこなっています。

その結果として、腸内細菌の種の多様性は年齢とともに増加する傾向があるが、糖尿病を発症する群は2歳くらいで種の多様性の増加がサチるという現象を発見しました。

f:id:abrahamcow:20160623203028j:plain
https://www.cell.com/cell-host-microbe/fulltext/S1931-3128(15)00021-9より)

データセット
DIABIMMUNE
に公開されています。

本当は統計モデリングでこの説を検証したかったけど、いまのところノーアイデアなので、今回は可視化だけ。

Kostic らは種の多様性の指標として Chao 1 (https://www.ism.ac.jp/editsec/toukei/pdf/60-2-263.pdf)というのを使っているけど、Chao 1がなんでこの式になるのかよくわからなかったので、ぼくはエントロピーを使うことにします。

腸内細菌のでデータはDNAを便のサンプルからまとめて回収してきて、データベースに照会することで菌種を特定します。

このときDNAのひとつの断片が菌種 k に該当する確率を p_kと表します。

エントロピー

 - \sum_{k}p_k \log p_k

で定義されます。

このエントロピーをサンプルごとに計算してプロットしたのがこちらです。

f:id:abrahamcow:20190809212941p:plain

T1Dが糖尿病です。たしかにちょっとサチってる気がする。

サンプルごとの繰り返し測定を表現したくて、こんな図も書いてみた。

f:id:abrahamcow:20190809213421p:plain

R のコードを貼ります。

library(tidyverse)library(readxl)

tab0 <- read_tsv("~/data/Kostic/diabimmune_t1d_16s_otu_table.txt",skip=1)
meta <- read_excel("~/data/Kostic/diabimmune_t1d_wgs_metadata.xlsx")
abundance <- tab0 %>% 
  dplyr::select(starts_with("G"))%>% 
  mutate_all(as.numeric)%>% 
  t()


S_obs <- ncol(abundance)

S0 <- rowSums(abundance>0)
F1 <- rowSums(abundance==1)
F2 <- rowSums(abundance==2)

P <-(abundance+1)/rowSums(abundance+1)

dfalpha <- tibble(Gid_16S = row.names(abundance),diversity=S0+(F1^2)/(2*F2),
                  entropy=-rowSums(P*log(P)))
datout <- left_join(dfalpha,meta)%>% 
  drop_na()

theme_set(theme_bw(16))

ggplot(datout,aes(x=Age_at_Collection,y=entropy,colour=T1D_Status))+
  geom_point()+
  stat_smooth()

ggplot(datout,aes(x=Age_at_Collection, y = Subject_ID, colour=entropy))+
  geom_point(size=10, pch=15,alpha=0.8)+
  scale_color_gradient2(mid="gray", midpoint = mean(datout$entropy))+
  facet_grid(T1D_Status~.,scales ="free_y", space ="free")

だれか種の多様性の変化のモデリングやりませんか?

可視化で理解するKPIツリー:コンバージョンの寄与度分解

$
0
0

背景

昔Web系コンサルをやっていたときKPIツリーとかを習ったんだけどぼくはこれが苦手だった。

例えばあるサイトのコンバージョン(CV)はサイトへの訪問数(SS)×コンバージョンレート(CVR)だからコンバージョンというゴールを増やしたかったら、SSを増やすかCVRを増やすしかない。

 CV = CVR \times SS

f:id:abrahamcow:20191210224050p:plain
KPIツリー

こうやって順々に要素を順々に分解していけばコンバージョンを増やす方策をロジカルに考えられますね、というわけ。

でも実際にCVの増減に対して、CVRの変化の寄与とSSの変化の寄与、どっちが大きいんだみたいなことをちゃんと比較してるのはみたことない。

そこでここではそれをやってみようと思う。

計算

2つの異なる時期のCV、CV1とCV2があったとする。

前述のとおり、

 CV_1 = SS_1 \times CVR_1

CV_2 = SS_2 \times CVR_2

である。

CVの増減比は CV_2 / CV_1で表せる。

 CV_2/CV_1 = (SS_2 \times CVR_2) / (SS_1 \times CVR_1)

変形すると、

 CV2/CV1 = (SS2_/SS_1)\times (CVR_2/CVR_1)

両辺の対数をとると、

 \log(CV_2/CV_1)=\log(SS_2/SS_1)+\log(CVR_2/CVR_1)

となる。

つまり対数をとって考えると、CVの増減量(左辺)が、\log(SS_2/SS_1)というSSの増減量と、\log(CVR_2/CVR_1)というCVRの増減量の足し算に分解できる。

実践

これは実際に対数をとったCVの増減比をCVRの変化の寄与とSSの変化の寄与に分解した図です。

黒い折れ線がCVの増減、棒グラフがそれに対するCVRとSSの寄与を示す。

f:id:abrahamcow:20191210225858p:plain

201809をベースラインとしてそこからの増減を示している。

0よりおおきければ増加、小さければ減少である。

201901のCV増加なんかはSSの寄与のみに支えられて、CVRはマイナスだったことがわかる。

201909なんかはCVRはほぼ横ばいなので、CVの減少要因はSSの減少に絞られる。

ただし、SSの減少要因はさらにセッション数を様々な方法で層別(ドリルダウン)して調べないとわからない。

上の図を書いたRのコードを貼ります。

なにかの参考になるでしょうか。

library(googleAnalyticsR)library(tidyverse)

ga_auth()
account_list <- ga_account_list()
ga_id <- account_list$viewId[3]

gadata <-
  google_analytics(ga_id,
                   date_range = c("2018-09-01","2019-11-30"),
                   metrics = c("sessions","goal3Completions"),
                   dimensions = c("yearMonth"))

logcont <-function(x, n){
  cj <- log(x)-log(n-x)
  be <- c(cj[-1]- cj[1])
  lN <- log(n)
  be2 <- c(lN[-1]- lN[1])
  lx <- log(x)
  be3 <- c(lx[-1]- lx[1])data.frame(CV=be3,SS=be2,CVR=be)}

out <- logcont(gadata$goal3Completions,gadata$sessions)
out$yearMonth <- gadata$yearMonth[-1]

out2 <- gather(out,key,value,-CV,-yearMonth)

ggplot(out2,aes(x=yearMonth))+
  geom_col(aes(y=value, fill=key))+
  geom_line(aes(y=CV, group=1),size=1)+
  geom_hline(yintercept =0,linetype=2)+
  theme_classic(20)+
  theme(axis.text.x = element_text(angle=90),axis.text = element_text(colour="black"))

関連エントリ

r×2分割表のオッズ比をロジスティック回帰っぽく(閉形式で)計算する関数を書いた - 廿TT

棒グラフをとにかくいっぱい並べて書きたい

$
0
0

f:id:abrahamcow:20200228222814p:plain

set.seed(2)
y <- lapply(1:7, rexp, n=26)
names(y)<- sapply(1:7,function(x)paste0(sample(LETTERS,10),collapse =""))
tab <-list(y=y,x=LETTERS)
class(tab)<-"minibarTable"

plot.minibarTable <-function(tab,...){
  oldpar <- graphics::par(no.readonly =TRUE)
  N <- length(tab$y)
  graphics::par(mar = c(4,10,0,2), oma = rep(1,4))
  graphics::layout(mat =matrix(1:N), respect =FALSE)for(i in1:N){
    tmpy <- tab$y[[i]]
    graphics::plot.default(1:length(tmpy),tmpy,
                           ylim = c(0,max(tmpy)),
                           type ="h",
                           xaxt="n", yaxt="n",
                           xlab ="", ylab ="", 
                           frame.plot =FALSE, ...)
    graphics::abline(h=0)
    graphics::points(1:length(tmpy),tmpy, pch =16, cex =0.9)
    ran <-c(0,max(tmpy))
    graphics::axis(side=4,at=format(ran,digits =2),las=2)
    graphics::mtext(names(tab$y[i]),side=2,las=2)}
  graphics::axis(side=1,at=1:length(tmpy),labels = tab$x,lwd=0)
  graphics::par(oldpar)}

plot(tab)

geom_pointrangeみたいなやつを並べて書きたい

$
0
0

f:id:abrahamcow:20200229082142p:plain

set.seed(1)
meanse <- lapply(1:7,function(i)cbind(mean=rnorm(10,0,2),se=rexp(10)))
names <- sapply(1:10,function(i)paste0(sample(LETTERS,10),collapse =""))
tab <-list(meanse=meanse,names=names)
class(tab)<-"seTable"

plot.seTable <-function(tab,layoutMat=matrix(1:length(tab$meanse),ncol =1),
                         alpha=0.05,leftmargin=7, col="black"){
  q <- qnorm(1-alpha/2)
  oldpar <- graphics::par(no.readonly =TRUE)
  N <- length(tab$meanse)
  ran <- range(sapply(tab$meanse,function(x)c(x[,1]-q*x[,2],x[,1]+q*x[,2])))
  graphics::par(mar = c(1.5, leftmargin,1.5,0), oma = rep(1,4))
  graphics::layout(mat = layoutMat, respect =FALSE)for(i in1:N){
    tmp <- tab$meanse[[i]]
    n <- nrow(tmp)
    plot.default(tmp[,1],1:n,xlim = ran,pch=16,
                 xaxt="n", yaxt="n",
                 xlab ="", ylab ="", 
                 main = paste("topic",i),
                 frame.plot =FALSE)
    segments(tmp[,1]-q*tmp[,2],1:n,tmp[,1]+q*tmp[,2],1:n)
    axis(side=2,at=1:n,labels = tab$names,las=2,lwd=0)
    abline(v=0,lty=2)if(i %% nrow(layoutMat)==0| i==N){
      axis(side=1)}}
  graphics::par(oldpar)}

plot(tab,matrix(1:8,ncol=2))

ggplot2 などのモダンなパッケージを使わずにトピックモデルのパラメータを可視化したい

$
0
0

腸内細菌のデータを使います。

説明は気が向いたら書きます。

f:id:abrahamcow:20200229083037p:plain

f:id:abrahamcow:20200229083117p:plain

library(curatedMetagenomicData)
plot.minibarTable <-function(tab,layoutMat,leftmargin=15, col="black"){
  oldpar <- graphics::par(no.readonly =TRUE)
  N <- length(tab)
  ran <-c(0,max(sapply(tab,function(x)max(x$value))))
  graphics::par(mar = c(1.5, leftmargin,1.5,0), oma = rep(1,4))
  graphics::layout(mat = layoutMat, respect =FALSE)for(i in1:N){
    tmpy <- tab[[i]]$value
    graphics::plot.default(rev(tmpy),1:length(tmpy),
                           xlim = ran,
                           type ="p",pch=16,
                           xaxt="n", yaxt="n",
                           xlab ="", ylab ="", 
                           frame.plot =FALSE,main = paste("topic",i),
                           col=col)
    graphics::segments(numeric(length(tmpy)),1:length(tmpy),rev(tmpy),1:length(tmpy), pch =16, cex =0.9,col=col)
    graphics::axis(side=2,at=1:length(tmpy),labels = tab[[i]]$name,las=2,lwd=0)
    abline(v=0)if(i %% nrow(layoutMat)==0| i==N){
      graphics::axis(side=1,at=format(ran,digits =2), lwd=0, lwd.ticks =1)}}
  graphics::par(oldpar)}
annotationBarplot <-function(W,annotation,hilight.pos=FALSE, hilight.col="gray10",cols=NULL){
  u <- unique(annotation)
  N <- nrow(W)
  bp <- barplot(W,space =0,plot =FALSE)if(is.null(cols)){
    cols <- grey.colors(N)if(hilight.pos[1]){
      cols <- rep("gray90",N)}}
  cols[hilight.pos]<- hilight.col
  bp <- barplot(W,plot=FALSE)
  bp <- barplot(W,xlim = c(0,max(bp)+12),legend.text =1:nrow(W), col = cols,
                args.legend =list(x ="topright", bty ="n"),border = cols,axes =FALSE)
  axis(2,at=c(0,1),las=2)for(i in1:length(u)){
    axis(side =1,at=range(bp[u[i]==annotation]),labels = c("",""),
         las=2,lwd=2)
    axis(side =1,at=mean(range(bp[u[i]==annotation])),labels = as.character(u[i]),
         las=1,lwd=0)}}
VBDirMult <-function(Y,L=2,alpha=rep(1,ncol(Y)),beta=rep(1,L),maxit=1000,seed=1){
  set.seed(1)
  N <- nrow(Y)
  K <- ncol(Y)
  EelW <-matrix(rgamma(N,beta),N,L,byrow =TRUE)
  EelW <- EelW/rowSums(EelW)
  EelH <-matrix(rgamma(L,alpha),L,K,byrow =TRUE)
  EelH <- EelH/rowSums(EelH)for(iter in1:maxit){
    Sw <- EelW *(((Y)/(EelW %*% EelH))%*% t(EelH))
    Sh <- EelH *(t(EelW)%*%(Y/(EelW %*% EelH)))
    beta_W <- beta + Sw
    alpha_H <- alpha + Sh
    EelW <-exp(sweep(digamma(beta_W),1,digamma(rowSums(beta_W))))
    EelH <-exp(sweep(digamma(alpha_H),1,digamma(rowSums(alpha_H))))}
  EW <- sweep(beta_W,1,rowSums(beta_W),"/")
  EH <- sweep(alpha_H,1,rowSums(alpha_H),"/")
  ELBO <- sum(lgamma(rowSums(Y)+1))- sum(lgamma(Y+1))+
    sum(-Y*(((EelW*log(EelW))%*%EelH + EelW%*%(EelH*log(EelH)))/(EelW%*%EelH)-log(EelW%*%EelH)))+
    L*sum(lgamma(sum(alpha))-sum(lgamma(alpha)))+
    sum(-lgamma(rowSums(alpha_H))+rowSums(lgamma(alpha_H)))+
    N*sum(lgamma(sum(beta))-sum(lgamma(beta)))+
    sum(-lgamma(rowSums(beta_W))+rowSums(lgamma(beta_W)))return(list(W=EW,H=EH,ELBO=ELBO))}

makeTopTable <-function(H,n=5){
  toplist <- apply(H,1,function(x){
    top = sort(x,decreasing =TRUE)[1:n]data.frame(name=factor(names(top),levels = names(top)),
               value=unname(top))})  
  class(toplist)<-"minibarTable"return(toplist)}

Zeller <- curatedMetagenomicData("ZellerG_2014.metaphlan_bugs_list.stool",
                       counts=TRUE, dryrun=FALSE)
Zeller.counts = exprs(Zeller$ZellerG_2014.metaphlan_bugs_list.stool)
splitnames <- strsplit(rownames(Zeller.counts),"\\|")
len <- sapply(splitnames, length)
Zeller.counts.g <- Zeller.counts[len==6,]
rownames(Zeller.counts.g)<- sapply(splitnames[len==6],function(x)x[6])

disease <- factor(Zeller$ZellerG_2014.metaphlan_bugs_list.stool$disease,levels=c("CRC","adenoma","healthy"))
ord <- order(disease)
disease <- disease[ord]
out <- VBDirMult(t(Zeller.counts.g)[ord,],L=5)

top5list <- makeTopTable(out$H)
mat <-matrix(1:5,ncol=1)
plot(top5list,mat,leftmargin =16,col="firebrick")

W <- t(unname(out$W))
ag <- apply(W,1,function(x)tapply(x, disease, median))
colnames(ag)<-1:5
mosaicplot(ag,cex.axis =0.9,main="",color ="cornflowerblue")

annotationBarplot(W,annotation = disease,cols = RColorBrewer::brewer.pal(5,"Greens"))

Turnbull のアルゴリズム(R の survival パッケージ)を用いた新型コロナウイルスの潜伏期間の推定

$
0
0

COVID-19 の潜伏期間をrstanで推定する - 驚異のアニヲタ社会復帰の予備の紹介で知った。

https://docs.google.com/spreadsheets/d/1jS24DjSPVWa4iuxuD4OAXrE3QeI8c9BC1hSlqr-NMiU/edit#gid=1449891965に,感染者が新型コロナウイルスのある地域への暴露を開始した日付と終了した日付,発症した日付が記録されている。

ここから新型コロナウイルスの潜伏期間を推定できるだろうか。

感染した日はわからないが,暴露を開始した日付と終了した日付の間のどこかに落ちる。

患者  iが発症した日を原点 0 として, 暴露が終了した日を  t_i日前とする。 暴露が終了した日からさかのぼって  s_i日前が感染した日だとする。

f:id:abrahamcow:20200321143627p:plain

この記法のもとで,もし感染した日がわかっている完全な観測が得られた場合, 発症までの待ち時間の確率密度は  f(s_i+t_i)である。

いま  s_iは未観測なので積分消去する。

感染者  i新型コロナウイルスのある地域へ暴露していた期間を  u_iとすると,ある患者 iの観測についての尤度は,

\int_0 ^{u_i} f(s_i+t_i) \,ds_i

である。 s_i+t_iをあらためて x_iとおくとこの積分

\int_{t_i} ^{u_i+t_i} f(x_i) \,dx_i

と書き換えられる。

これは,生存時間分析の分野で区間打ち切りとよばれる観測と同じ。

区間打ち切りされたデータからノンパラメトリックに生存関数(1引く分布関数)を求める方法はすでに提案されており, R では survival パッケージで提供されている。

abrahamcow.hatenablog.com

で以前に作った関数群で新型コロナウイルスの感染履歴データを視覚化してみる。

f:id:abrahamcow:20200321145057p:plain
潜伏期間の生存関数の推定値

半透明の帯は95%信頼区間

f:id:abrahamcow:20200321145124p:plain
潜伏期間の密度関数の推定値(ヒストグラム

エラーバーは95%区間(2.5パーセンタイルから97.5パーセンタイル)。だいたい2日から19日。

WHOの見積もった潜伏期間(新型コロナウイルスに関するQ&A(一般の方向け)|厚生労働省)1〜12.5日より右によってる。

library(survival)library(tidyverse)library(tidysurv)library(readxl)library(cowplot)

ct <- rep("guess",23)
ct[8]<-"numeric"
ct[c(3,9,11,14,15,19,20,21)]<-"date"#データは冒頭のリンク
data_new <- read_excel("~/Downloads/Kudos to DXY.cn Last update_ 03_13_2020,  8_00 PM (EST).xlsx",sheet =1,skip =1,
                       col_types= ct)
data_new2 <- data_new %>%
  dplyr::filter(!is.na(symptom_onset)&!is.na(exposure_end))%>%
  mutate(exposure = as.integer(as.Date(exposure_end)-as.Date(exposure_start)),
         incubation = as.integer(as.Date(symptom_onset)- as.Date(exposure_end)))%>%
  dplyr::filter(incubation >0)#生存関数の推定はこの1行をかくだけ
sf1 <- survfit(Surv(time=incubation,time2=exposure+incubation, type="interval2")~1, data=data_new2)

dfsf <- tidysurv(sf1)
ggplot(dfsf,aes(x=time,y=survival,ymin=lower,ymax=upper))+
  geom_line()+
  geom_ribbon(alpha=0.3)

bw <-2.0
dfh <- make_hdf(sf1,bw)
qdf <- make_qdf(sf1,probs=c(0.025,0.975))
ggplot(dfh)+
  geom_col(aes(x=midtime,y=density),width = bw)+
  geom_errorbarh(data=qdf,aes(xmin=`q 2.5`,xmax=q97.5,y=-0.01,height=0.01))+
  xlim(c(-0.1,NA))+labs(colour="")

参考文献はなに貼るか迷うな。

とりあえずこれにしとくが,めっちゃおすすめというわけでもない。

生存時間解析

生存時間解析

パラメトリックにやる場合には次のようにする:

abrahamcow.hatenablog.com