中心極限定理を確かめる

どんな分布(注)からでもサンプリングされた標本の平均をさらに繰り返して標本の分布を考えたとき、その分布は正規分布になる。
というのでいろいろ分布を変えて試してみる。
Rでは接頭語rdpqに続いて分布名があるので、標準で実装されている関数をapropos関数で取得すると、19個あるようだった。

 [1] "beta"     "binom"    "cauchy"   "chisq"    "exp"      "f"        "gamma"    "geom"     "hyper"   
[10] "lnorm"    "logis"    "nbinom"   "norm"     "pois"     "signrank" "t"        "unif"     "weibull" 
[19] "wilcox"  

19個の関数で必要なパラメータを適当に決め、30個サンプリングして平均値を求め、これを3000回繰り返すとする。
関数からサンプリングした標本の標本平均と標本分散はデータから決まるし、関数とパラメータから理論的な平均と分散も計算できるのでこれも計算する。
乱数発生関数に引数をいれるときに無理やりテキスト形式で

rnorm(n, mean, sd)

を実現するために、テキスト処理をしたあとにparse関数とeval関数で無理やり評価して関数を実行している。

ヒストグラムの設定が適当なせいなのか理論的な分布との重ね合わせが合ってないが、だいたいが平均値の分布は正規分布に従っている。
ここで、コーシー分布がおかしいことになっているが、コーシー分布は平均値が定義できない、となっているので、おかしいのが正しい。

library(stringr)
rdpq <- c("r", "d", "p", "q")
fs <- mapply(function(z) str_remove(apropos(sprintf("^%s", z)), sprintf("^%s", z)), rdpq)

f <- fs[[1]]
for(i in 1:(length(fs)-1)){
  for(j in (i+1):length(fs)){
    f <- intersect(f, intersect(fs[[i]], fs[[j]]))
  }
}

n <- 30 # ある分布からのサンプリング数
p <- ev <- replicate(length(f), vector("list"))
names(p) <- names(ev) <- f

p[[f[1]]] <- c(10, 20)    # beta
p[[f[2]]] <- c(20, 0.2)   # binom
p[[f[3]]] <- ""           # cauchy
p[[f[4]]] <- c(5)         # chisq
p[[f[5]]] <- c(2)         # exp
p[[f[6]]] <- c(4, 6)      # f
p[[f[7]]] <- c(0.5, 2)    # gamma
p[[f[8]]] <- c(0.3)       # geom
p[[f[9]]] <- c(5, 10, 12) # hyper
p[[f[10]]] <- c(0.3, 1.5) # lnorm
p[[f[11]]] <- c(10, 3.3)  # logis
p[[f[12]]] <- c(10, 0.25) # nbinom
p[[f[13]]] <- c(6, 3.5)   # norm
p[[f[14]]] <- c(5)        # pois
p[[f[15]]] <- c(10)       # signrank
p[[f[16]]] <- c(4)        # t
p[[f[17]]] <- c(2.8, 9.4) # unif
p[[f[18]]] <- c(4, 3)     # weibull
p[[f[19]]] <- c(4, 7)     # wilcox

niter <- 3000 # n回サンプリングするシミュレーションの試行回数
res <- replicate(niter,
colMeans(
mapply(function(z0, z1){
         eval(parse(text=sprintf("r%s(%d%s)", z0, n, paste0(ifelse(all(z1==""), "", ","), paste0(z1, collapse=",")))))
        }, f, p)
)
)

xq <- seq(0, 1, length=10000)
dens <- mapply(function(z0, z1){
         txt <- sprintf("q%s(%f%s)", z0, xq, paste0(ifelse(all(z1==""), "", ","), paste0(z1, collapse=",")))
         y <- mapply(function(w) eval(parse(text=w)), txt)
         d <- mapply(function(w) eval(parse(text=sprintf("d%s(%f%s)", z0, w, paste0(ifelse(all(z1==""), "", ","), paste0(z1, collapse=","))))), y)
         return(list(q=y, d=d))
        }, f, p, SIMPLIFY=FALSE)

ev[["beta"]] <- c(
  E=p[["beta"]][1]/(p[["beta"]][1]+p[["beta"]][2]),
  V=p[["beta"]][1]*p[["beta"]][2]/((p[["beta"]][1]+p[["beta"]][2])^2*(p[["beta"]][1]+p[["beta"]][2]+1))
  )

ev[["binom"]] <- c(
  E=prod(p[["binom"]]),
  V=p[["binom"]][1]*p[["binom"]][2]*(1-p[["binom"]][2])
  )

ev[["cauchy"]] <- c(
  E=NA,
  V=NA
  )

ev[["chisq"]] <- c(
  E=p[["chisq"]][1],
  V=2*p[["chisq"]][1]
  )

ev[["exp"]] <- c(
  E=1/p[["exp"]][1],
  V=(1/p[["exp"]][1])^2
  )

ev[["f"]] <- c(
  E=ifelse(p[["f"]][2] > 2, p[["f"]][2]/(p[["f"]][2]-2), NA),
  V=ifelse(p[["f"]][2] > 4, 2*p[["f"]][2]^2*(sum(p[["f"]])-2)/p[["f"]][1]/(p[["f"]][2]-2)^2/(p[["f"]][2]-4), NA)
  )

ev[["gamma"]] <- c(
  E=prod(p[["gamma"]]),
  V=p[["gamma"]][1]*p[["gamma"]][2]^2
  )

ev[["geom"]] <- c(
  E=1/p[["gamma"]][1],
  V=(1-p[["gamma"]][1])/(p[["gamma"]][1]^2)
  )

ev[["hyper"]] <- c(
  E=p[["hyper"]][3]*p[["hyper"]][1]/(p[["hyper"]][1]+p[["hyper"]][2]),
  V=p[["hyper"]][3]*p[["hyper"]][1]/(p[["hyper"]][1]+p[["hyper"]][2])*(1-p[["hyper"]][1]/(p[["hyper"]][1]+p[["hyper"]][2]))*(p[["hyper"]][1]+p[["hyper"]][2]-p[["hyper"]][3])/(p[["hyper"]][1]+p[["hyper"]][2]-1)
  )

ev[["lnorm"]] <- c(
  E=exp(p[["lnorm"]][1]+p[["lnorm"]][2]/2),
  V=exp(2*p[["lnorm"]][1]+p[["lnorm"]][2])*(exp(p[["lnorm"]][2])-1)
  )

ev[["logis"]] <- c(
  E=p[["logis"]][1],
  V=pi^2*p[["logis"]][2]^2/3
  )

ev[["nbinom"]] <- c(
  E=p[["nbinom"]][1]*(1-p[["nbinom"]][2])/p[["nbinom"]][2],
  V=p[["nbinom"]][1]*(1-p[["nbinom"]][2])/p[["nbinom"]][2]^2
  )

ev[["norm"]] <- c(
  E=p[["norm"]][1],
  V=p[["norm"]][2]
  )

ev[["pois"]] <- c(
  E=p[["pois"]][1],
  V=p[["pois"]][1]
  )

ev[["signrank"]] <- c(
  E=p[["signrank"]][1]*(p[["signrank"]][1]+1)/4,
  V=p[["signrank"]][1]*(p[["signrank"]][1]+1)*(2*p[["signrank"]][1]+1)/24
  )

ev[["t"]] <- c(
  E=ifelse(p[["t"]][1] > 1, 0, NA),
  V=ifelse(p[["t"]][1] > 2, 1/(1-2/p[["t"]][1]), NA)
  )

ev[["unif"]] <- c(
  E=mean(p[["unif"]]),
  V=diff(p[["unif"]])^2/12
  )

ev[["weibull"]] <- c(
  E=p[["weibull"]][2] * gamma(1 + 1/p[["weibull"]][1]),
  V=p[["weibull"]][2]^2 * (gamma(1 + 2/p[["weibull"]][1]) - (gamma(1 + 1/p[["weibull"]][1]))^2)
  )

ev[["wilcox"]] <- c(
  E=prod(p[["wilcox"]])/2,
  V=prod(p[["wilcox"]])*(sum(p[["wilcox"]])+1)/12
  )
ev <- lapply(ev, "*", c(1, 1/n))

# 分布からの推定と理論値の確認
m <- rowMeans(res)
v <- apply(res, 1, var)
a <- cbind(do.call(rbind, ev), m=m, v=v)

cols <- c("distribution"="orange", "theorem"="blue")
par(mfrow=c(length(f), 2), cex.main=2)
for(i in seq(f)){
  ml <- list(m[i], ev[[i]]["E"])
  vl <- list(v[i], ev[[i]]["V"])
  de <- mapply(function(z1, z2) mapply(qnorm, xq, z1, z2), ml, vl, SIMPLIFY=FALSE)
  y <- mapply(function(z0, z1, z2) mapply(dnorm, z0, z1, z2), de, ml, vl, SIMPLIFY=FALSE)
  yl <- c(0, max(unlist(y), na.rm=TRUE))
  hist(res[i,], probability=TRUE, xlab="mean", ylab="density", main=f[i], ylim=yl, cex.main=2)
  for(j in seq(y)){
    lines(de[[j]], y[[j]], col=cols[j], lwd=5)
  }
  legend("topright", legend=names(cols), col=cols, pch=15, cex=2)
  plot(dens[[i]]$q, dens[[i]]$d, type="l", lwd=5, xlab="", ylab="density", main=sprintf("%s distribution", f[i]))
}

BeyondER キャリアと心不全

読んだ。

BeyondER(ビヨンダー) Vol.2 No.2 2023

BeyondER(ビヨンダー) Vol.2 No.2 2023

  • メディカル・サイエンス・インターナショナル
Amazon
BeyondER (2巻2号) | 医書.jp
同級生が書いてて偉くなったもんだなと感心した。

みんな知ってる有名人の寺澤先生の対談があった。
日本海側のバイトに行ったときに外来でいて少しだけ話ししたことがあるが、穏やかな先生だった。
組織のメンバーを大事にしないと、その後輩が噂を聞いてその組織を忌避して組織が盛り下がっていく、というような話は参考になった。

新興雑誌のため他の救急系の雑誌と毛色を変えたいのか、今回は専門医取得以降のキャリアパスについて紹介されていた。
2.救急医がMBAホルダーになって見えてきたもの (BeyondER 2巻2号) | 医書.jp
「8年目のジレンマ」というのは確かに筆者の言うようにたぶん専門医取得以降の若者()が陥りがちな事態だと思う。この本で紹介されている学位はPh.D 以外は2年を費やして仮題をこなせば取りあえず取れるものなので、やる気があればなんとかなる。
4.Ph.D.への挑戦が,ゼロからイチを生み出す力を向上させる (BeyondER 2巻2号) | 医書.jp
Ph.Dについては必ずしも取れるわけではないので、それなりに覚悟は必要である。よく名前を聞く先生だが論文はあるものの学位は日本語でまとめて論文博士らしい。解散。

心不全については最近流行りのANRI などの解説があり勉強になった。

症例報告・基礎研究・臨床研究の論文の書き方

小児科診療 (83巻7号) | 医書.jp
症例報告だけでも医者人生が変わるという華々しい話が面白かった。

臨床婦人科産科 (74巻11号) | 医書.jp

臨床雑誌整形外科 (71巻6号) | 医書.jp
整形外科医はこういう話に詳しくないだろう(失礼)と思うが、臨床研究の仕組みだとかいろいろ詳しく書いてある。

臨床放射線 (64巻12号) | 医書.jp
放射線領域なのであまり馴染みがなかった。

腎と透析 (93巻3号) | 医書.jp
研究不正の話が面白かった。

Dr.徳田の英語論文 書き方のツボ
(第1回)タイトルを付ける 査読者を惹き付ける3つのポイント (臨床雑誌内科 113巻1号) | 医書.jp
サマリー(アブストラクト)をまとめる 刑事コロンボ的組立てとオッカムのかみそり (臨床雑誌内科 113巻2号) | 医書.jp
BACKGROUNDを書く 症例の重要性は"三段論法"でまとめよう (臨床雑誌内科 113巻3号) | 医書.jp
CASE DESCRIPTION(その1) 病歴を書く わかりやすい病歴は「シ・ゲ・キ+α」が重要 (臨床雑誌内科 113巻4号) | 医書.jp
CASE DESCRIPTION(その2) 身体所見を書く 身体所見は「バイタル+陽性所見+陰性所見」の3つが基本 (臨床雑誌内科 113巻5号) | 医書.jp
CASE DESCRIPTION(その3) 検査所見を書く 検査所見は「血液・尿+画像+診断検査」の3つが基本 (臨床雑誌内科 114巻1号) | 医書.jp
CASE DESCRIPTION(その4) 図表の作成 画像は「キーフィルム+病変部位に原色矢印を」が基本 (臨床雑誌内科 114巻2号) | 医書.jp
CASE DESCRIPTION(その5) 診断・治療・経過 症例報告では診断が確定していることが必須 (臨床雑誌内科 114巻3号) | 医書.jp
DISCUSSIONを書く 症例報告の正当性を過去の先行報告と比較してまとめよう (臨床雑誌内科 114巻4号) | 医書.jp
文献リスト(REFERENCES) 引用論文はできるだけ一次文献としよう (臨床雑誌内科 114巻5号) | 医書.jp
アブストをまとめる 読者を堪能させる3つのポイント (臨床雑誌内科 115巻5号) | 医書.jp
イントロを書く 読者を納得させる3つのポイント (臨床雑誌内科 116巻1号) | 医書.jp
メソッドを書く 論文の妥当性と信頼性を示す3つのポイント (臨床雑誌内科 116巻2号) | 医書.jp
リザルツを書く 論文の結果とその詳細を示す3つのポイント (臨床雑誌内科 116巻3号) | 医書.jp
図表を作成する シンプルな図表で読者を惹きつける3つのポイント (臨床雑誌内科 116巻4号) | 医書.jp
ディスカッションを書く(前編) 論文の主張を述べる4つのポイント (臨床雑誌内科 116巻5号) | 医書.jp
ディスカッションを書く(後編) 論文の主張を述べる4つのポイント (臨床雑誌内科 117巻1号) | 医書.jp
文献リスト(References)をつくる エビデンスの引用元を示す3つのポイント (臨床雑誌内科 117巻2号) | 医書.jp
実際に論文で使った英文の例が載っていた。

世のため人のため自分のため英語論文を書こう (臨床整形外科 53巻9号) | 医書.jp
書籍は読んでいないが、連載では投稿先の選び方や原稿の書き方、図作成ソフトや英語の勉強法まで、分かっている人なら「そんなこと説明する必要ある?」と思うが本当に初心者ならわからないであろうことまで、言及してあったのでそれは良かったと思う。

英文論文を書いてみよう!—なかなか書けない外科医のための集中講義
なぜ研究をするのか? (臨床外科 73巻1号) | 医書.jp
なぜ論文を書くのか? (臨床外科 73巻2号) | 医書.jp
論文の基本構成 (臨床外科 73巻3号) | 医書.jp
論文のbrush-up (臨床外科 73巻4号) | 医書.jp
査読者の立場から (臨床外科 73巻5号) | 医書.jp
とにかく書いてみよう (臨床外科 73巻6号) | 医書.jp
初回の、教授になったときにこれくらい論文があった、というのは興味深かった。

英語で論文を書くことの効能……学会発表だけじゃダメなんですか?
その1.学会発表と論文はどう違うの?…① (臨牀消化器内科 35巻3号) | 医書.jp
1Pのコラム。全12回。

いろいろ読んだけどこの間に論文は1本も出ていない(爆

透析と麻酔

読んだ。

LiSA別冊: 周術期管理を核とした総合誌 (Vol.30 ’23 秋号)

LiSA別冊: 周術期管理を核とした総合誌 (Vol.30 ’23 秋号)

  • メディカル・サイエンス・インターナショナル
Amazon
LiSA 別冊 (30巻2号) | 医書.jp
けっこう良かった。
シャント肢の手背や尺側にしばしばよい血管が残っているので特に緊急時は躊躇なく使用すればよい。ただし複雑な血流となっているため、薬物投与時の反応が遅延する場合がある。
単回ならシャント肢で血圧を測定することもあるが、マンシェットを巻いたままにしてはいけない。
シャント位置より抹消(手首など)で測定できる場合は、マンシェットはそのままで使用してよい。
ガイドラインではKを含まない1号液を選択するよう記載しているものもあるが、投与量が増えると低Na血症となるためやはり細胞外液の投与が望ましい。
生食とリンゲル液では、腎移植患者でのRCTでは最高K濃度は差がなく、生食のほうでpHが低くClが高いという高クロール性代謝性アシドーシスとなっていた。
乳酸化リンゲル液は4mEq/L のKを含むので大量に投与すればその値に近づく。
体重50kgの成人にRBC 8単位を負荷するとK 4→5 mmol/L に上昇すると推測される。

など、管理上これこれするな/しろ、と言われるが本当にそうなのか、ということへのちょっとした情報が載っていた。

ついでに読んだ。
術者がうますぎて人工心肺時間が短すぎるため離脱に困ることがほとんどないのだが、様々な事例が載っており参考になった。
LiSA (30巻10号) | 医書.jp

Heart View でいくつか読んだもの

Heart View (27巻9号) | 医書.jp
Heart View (27巻2号) | 医書.jp
Heart View (26巻13号) | 医書.jp

これはすごいよかった。と言ってもいまだに不整脈よくわかっていないが。

Heart View (26巻12号) | 医書.jp
Heart View (26巻3号) | 医書.jp
Heart View (24巻9号) | 医書.jp
Heart View (24巻4号) | 医書.jp
Heart View (21巻5号) | 医書.jp
Heart View (21巻4号) | 医書.jp

ステロイド

恥ずかしながらステロイドについてあまり知らないので読んだ。

JOHNS (39巻4号) | 医書.jp
こちらのほうが薬理的な話も耳鼻科領域での使用法も詳しかった。耳鼻咽喉科・頭頸部外科 (93巻9号) | 医書.jp
ページ数が少ないのですぐ読める。

藤井棋士の生存曲線

奨励会時の昇級が分かったので追記した。

藤井棋士が最後の王座戦に勝利し、全タイトル戦を制覇し八冠となった。
ここで、八冠になるまでに藤井〇〇という称号がいくつかあるわけだが、それぞれを個別の藤井棋士とみなすと、称号保持の期間がどれくらいあるか日数が出る。
というわけで生存曲線を作ってみる。
日本棋院wikiを参考に、藤井棋士の段位や称号を得た時期をまとめる。アマチュア時代の級はよくわからなかったので奨励会に入るまでがタダの棋士奨励会時代は六級から一級まで(このシステムはよくわかっていない)、一段からの昇段日をまとめた。
全部で32人の藤井〇〇という称号があるようだった。
ただし、実際の生存解析では各個人は独立である。藤井竜王・名人は藤井竜王、藤井名人であることを含んでいるし、藤井N冠はN-1冠すべてを含んでいるので各人は独立ではない。
直近のタイトル戦では12月7日の竜王戦まで4連敗するまでは八冠のタイトルを保持できるから、2023年12月7日まで八冠と仮定した。
中央生存日数で296日は八冠でいられるようだ。
MSTの信頼区間の計算はBrookmeyer & Crowley の手法を使った。
https://www.jstor.org/stable/2530286
Kaplan-Meier Survival Estimates (Survival Curves) - StatsDirect
統計学入門−第11章



library(survival)
library(prodlim)
txt <- "
class,title,start,end
出生,ama,2002-07-19,2012-09-21
六級,ama,2012-09-22,2012-11-09
五級,ama,2012-11-10,2013-05-04
四級,ama,2013-05-05,2013-06-01
三級,ama,2013-06-02,2013-09-14
二級,ama,2013-09-15,2014-04-04
一級,ama,2014-04-05,2014-06-20
一段,dan,2014-06-21,2015-02-27
二段,dan,2015-02-28,2015-10-17
三段,dan,2015-10-18,2016-09-30
四段,dan,2016-10-01,2018-01-31
五段,dan,2018-02-01,2018-02-16
六段,dan,2018-02-17,2018-05-17
七段,dan,2018-05-18,2020-08-19
八段,dan,2020-08-20,2021-07-02
九段,dan,2021-07-03,
棋聖,title,2020-07-16,
王位,title,2020-08-20,
叡王,title,2021-09-13,
竜王,title,2021-11-13,
王将,title,2022-02-12,
棋王,title,2023-03-19,
名人,title,2023-06-01,
竜王・名人,title,2023-06-01,
王座,title,2023-10-11,
二冠,kan,2020-08-20,2021-09-12
三冠,kan,2021-09-13,2021-11-12
四冠,kan,2021-11-13,2022-02-11
五冠,kan,2022-02-12,2023-03-18
六冠,kan,2023-03-19,2023-05-31
七冠,kan,2023-06-01,2023-10-10
八冠,kan,2023-10-11,
"

sysd <- Sys.Date()
# sysd <- as.Date("2023-12-07")
dat <- read.csv(text=txt, stringsAsFactors=FALSE)
dat$end <- ifelse(dat$end == "", as.character(sysd), dat$end)

dat$days <- as.numeric(as.Date(dat$end) - as.Date(dat$start))
dat$event <- (dat$end != sysd) + 0

fit <- survfit(Surv(days, event=event, type="right") ~ 1, data=dat)
sfit <- prodlim(Hist(days, event=event) ~ 1, data=dat)
ss <- summary(sfit)
s1 <- ss$table[ss$table[, "n.event"] == 0, ]
s2 <- merge(subset(dat[dat$days%in%s1[, "time"],], title=="title"), s1, by.x="days", by.y="time")

# MST の信頼区間の計算
g <- switch((is.null(fit$strata))+1, unlist(mapply(rep, names(fit$strata), fit$strata)), rep(1, length(fit$n.event)))
er <- tapply(fit$surv*fit$std.err, g, c)
p <- tapply(fit$surv, g, c)
t0 <- tapply(fit$time, g, c)
i0 <- lapply(p, function(z) tail(which(z > pai), 1))
i1 <- lapply(p, function(z) head(which(z < pai), 1))
j0 <- mapply(function(z0, z1) z0[z1], t0, i0, SIMPLIFY=FALSE)
j1 <- mapply(function(z0, z1) z0[z1], t0, i1, SIMPLIFY=FALSE)
mst <- mapply(function(z0, z1, z2, z3){z0[z1]+(z0[z2]-z0[z1])*(z3[z1]-pai)/(z3[z1]-z3[z2])}, t0, i0, i1, p)
cis <- do.call(rbind, list(
  mapply(function(z0, z1, z2, z3){z0[tail(which(z1+2*((z0>z2)-0.5)*qnorm(0.975)*z3 > pai & z0 < z2), 1)+1]}, t0, p, mst, er),
  mst,
  mapply(function(z0, z1, z2, z3){z0[head(which(z1+2*((z0>z2)-0.5)*qnorm(0.975)*z3 < pai & z0 > z2), 1)-1]}, t0, p, mst, er)
  )
)

half_time <- sfit$time[which(sfit$surv < 0.5)[1]]
xl <- c(0, 365*5)
axis1.at <- at.t <- floor(seq(0, ceiling(max(xl/365)), by=0.5)*365)
axis1.labels <- sprintf(ifelse(at.t%%365 == 0, "%.f", "%.1f"), at.t/365)

par(mar=c(4, 4, 1, 1), las=1, cex.lab=1.5)
plot(sfit, lwd=3, xlim=xl,
     xlab="タイトル保持期間(年)", ylab="",
     axis1.at=axis1.at, axis1.labels=axis1.labels,
     atrisk.at=at.t, marktime=TRUE, background=TRUE, background.horizontal=NA,
     atrisk.labels="藤井棋士",
     confint.col="yellow")
mtext("生存確率", 2, line=2, las=3, cex=1.5)
p <- par()$usr
# segments(half_time, 0.5, y1=p[3], lty=3)
# segments(half_time, 0.5, x1=-100, lty=3)
# text(half_time, p[3], sprintf("%d日", half_time), cex=2, adj=c(-0.1, -0.5))
arrows(cis[2, 1], 0.5, cis[1, 1], angle=90, length=0.1, col=4, lwd=3)
arrows(cis[2, 1], 0.5, cis[3, 1], angle=90, length=0.1, col=4, lwd=3)
segments(cis[2, 1], 0.5, y1=p[3], lty=3)
txt <- sprintf("%.f日 [95%s CI: %.f ー %.f]", cis[2, 1], "%", cis[1, 1], cis[3, 1])
text(half_time, p[3], txt, cex=2, adj=c(0, -0.5))
txt <- sprintf("%s年%d月%d日時点", format(sysd, "%Y"), as.numeric(format(sysd, "%m")), as.numeric(format(sysd, "%d")))
legend("topright", legend=txt, bty="n", cex=2)

# 生存確率が同じタイトルを同一x軸上にtext する
xd <- 100
r <- rle(s2[, "surv"])
xx <- unlist(mapply(rep, s2[cumsum(r$lengths), "days"], r$lengths))
dup <- duplicated(s2[, "days"]) | duplicated(s2[, "days"], fromLast=TRUE)
dif <- c(xd, diff(s2[, "days"]))
tcex <- 1.2
for(i in seq(nrow(s2))){
  x0 <- s2[i, "days"]
  y0 <- s2[i, "surv"]
  x1 <- xx[i] + xd
  if(i != 1){ # 王位と棋聖のように近いタイトルを上下離す
    flag <- 2*((!(dif < xd/2 & dif > 0))-0.5)[i]
    y1 <- y0 + (p[4]-p[3])*(x1-x0)/(p[2]-p[1])*flag
  } else {
    y1 <- y0
  }
  if(!dup[i]){
    txt <- sprintf("%s (%d日)", s2[i, "class"], s2[i, "days"])
    segments(x0, y0, x1, y1, lty=3, lwd=2)
    text(x1, y1, txt, pos=4, cex=tcex)
  } else {
    if(duplicated(s2[, "days"])[i]){ # 竜王・名人がかぶる
      txt <- sprintf("%s (%d日)", paste(s2[dup, "class"], collapse="/"), s2[i, "days"])
      segments(x0, y0, x1, y1, lty=3, lwd=2)
      text(x1, y1, txt, pos=4, cex=tcex)
    }
  }
}


d0 <- list(as.POSIXct(dat$start), as.POSIXct(dat$end))
interval <- list(seq(as.POSIXlt(format(min(d0[[1]]), "%Y-01-01")), max(d0[[2]]), by="year"),
seq(as.POSIXlt(format(min(d0[[1]]), "%Y-01-01")), max(d0[[2]]), by="month")
)
interval[[2]] <- setdiff(interval[[2]], interval[[1]])

y <- 1:nrow(dat)
xl <- as.numeric(c(as.POSIXlt("2012-04-01"), max(d0[[2]])))
yl <- c(1, nrow(dat))
cols <- c(ama="red", dan="yellow", title="cyan", kan="orange")

par(mar=c(2.5, 6, 2.5, 1.5))
plot(d0[[1]], y, type="n", xlim=xl, ylim=yl, xaxt="n", yaxt="n", xlab="", ylab="", bty="[")
axis(2, at=y, labels=dat$class, las=1)
axis(1, at=interval[[1]], labels=format(interval[[1]], "%Y"))
axis(3, at=interval[[1]], labels=format(interval[[1]], "%Y"))
abline(v=interval[[1]])
# abline(v=interval[[2]], lty=3) # 毎月
for(i in seq(nrow(dat))){
  #segments(x0=d0[[1]][i], y0=i, x1=d0[[2]][i], lwd=20)
  px <- c(d0[[1]][i], d0[[2]][i])[c(1,2,2,1)]
  py <- i + c(-1, -1, 1, 1)*0.5
  polygon(px, py, col=cols[dat$title[i]], border=NA)
  text(px[2], i, dat$day[i], pos=4, xpd=TRUE)
}
abline(h=c(0, y)+0.5, lty=3)