ラブライブ! サンシャインを見ていた。視聴前のキャラデザでは圧倒的に松浦果南、桜内梨子の圧勝だった。
13話についてはいろいろ思うところがあるだろうが、これまでに視聴していて気になったことがある。
『ラブライブ!サンシャイン!!』果南ちゃん出番なさすぎて人気出るか心配! | やらおん!
セリフがなかった回が3回もあるし、そもそも休学中という設定で学院での出番が他のキャラより少なくならざるをえないという圧倒的不遇ポジションである。千歌の幼なじみだったはずなのに…
中の人の諏訪ななかも、気にしているのか、6話放送時には
という発言をしている。
というわけで、本当に果南のセリフが少なくて不遇だったかをrstan で解析する。
サンシャイン全13話をひと通り視聴して、セリフの回数をカウントした。セリフを言っている、とは、誰かが喋ったあとにセリフがあるとそれを1回とし、一連のセリフが続いている間はそれがひとつのセリフだとみなした。ただし、場面が変わったり、回想が挟まれたら同一のキャラがしゃべっていても、セリフ回数が1回増えた、とする。
また、3人までなら、同時に返事したりリアクションしたりしたらその3人にセリフ1回カウントとする。2人でランニングしているときの息づかいは難しいので1回のみカウントする。
時間短縮のため2倍速で見ていたのでカウント漏れは多々ある、と思う。
とうわけで9人13話分のセリフ回数が以下である。
name v01 v02 v03 v04 v05 v06 v07 v08 v09 v10 v11 v12 v13 高海千歌 133 115 94 59 102 63 73 55 46 64 53 75 63 桜内梨子 34 65 68 30 87 44 50 34 21 36 19 28 28 渡辺曜 48 39 68 33 20 31 21 23 24 29 72 30 23 津島善子 13 2 3 1 65 21 28 16 12 24 9 10 18 国木田花丸 10 7 5 58 35 18 27 20 10 15 14 12 16 黒澤ルビィ 8 10 9 77 26 39 40 16 23 27 12 24 18 松浦果南 8 5 0 9 0 3 0 13 49 20 16 26 15 黒澤ダイヤ 17 18 11 12 8 24 14 19 24 41 18 31 19 小原鞠莉 1 0 27 9 3 19 4 13 44 22 27 15 15
果南は3, 5, 7話でセリフ0回だった。鞠莉も2話で0回だが、果南の不遇っぷりはやばい。千歌は主人公なのでしゃべりっぱなしである。
1話中のセリフ総数に占める割合を見ても、千歌は30% くらいのシェアを誇る。梨子、曜の2年生組で6割近く占める。
3年生は…ダイヤがんばれ。
rstan を使って、果南のセリフ回数が最下位になるのが有意になるのかどうかを考えよう。
モデル1 として、各話ごとでのセリフ総数 に対して、9人のキャラが確率 を満たすsimplex を用意し、各キャラのセリフ回数 について
としてサンプリングする。
セリフの総数は、アニメの実質放送時間20分くらいに、セリフが生じるとしたらポアソン過程である。パラメータは平均, 分散 となるので、正規分布にして を増やさなくていいのでこれでいくことにしよう。
コードはこんな感じになる。このモデルでは各話 ごとに が変わるとしている。
バーンイン 1000、サンプリング 2000、チェイン 3 で10秒以下で推定できる。収束の はすべて1 だった。
# model01 data{ int<lower=0> N; # キャラ人数 int<lower=0> V;; # 話数 int<lower=0, upper=1000> serif[V, N]; # セリフの数 } parameters{ simplex[N] theta[V]; # 総和1の得票率 real<lower=0, upper=1000> lambda; # 1話あたりのセリフ総数の平均 } model{ for(v in 1:V){ sum(serif[v,]) ~ poisson(lambda); serif[v,] ~ multinomial(theta[v]); # 多項分布からサンプリング } } generated quantities{ int s[V, N]; # セリフ数 int<lower=10> s_all[V]; # 1話ごとの総セリフ数 for(v in 1:V){ s_all[v] <- poisson_rng(lambda); s[v,] <- multinomial_rng(theta[v], s_all[v]); } }
generated quantities を使って、各iter でサンプリングして仮想データを作り、果南が最下位のセリフになる回数が何回あるかカウントすると
高海千歌 桜内梨子 渡辺曜 津島善子 国木田花丸 黒澤ルビィ 松浦果南 黒澤ダイヤ 小原鞠莉 0.000 0.000 0.000 0.057 0.003 0.000 0.822 0.000 0.118
13話通して、果南の総セリフ数が9人のなかで最下位になるのは、ベイス的には82% となる。
各話ごとに、各キャラがセリフ回数最下位になる確率は以下のとおりである。果南は6話で97.7% とほぼ確実にセリフ最下位である。6話では3回のセリフがあったが、2番目にセリフの少ない花丸で18回もセリフがあるため、果南の存在感が小さくなっている。
セリフ回数0 の3, 5, 7話では、同時に鞠莉もセリフ回数が少ないのでセリフ最下位確率は減るが、それでも80-90% くらいある。
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] 高海千歌 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 桜内梨子 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.043 0.003 0.036 0.002 0.005 渡辺曜 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.019 0.016 0.026 0.000 0.002 0.029 津島善子 0.009 0.291 0.199 0.931 0.000 0.006 0.000 0.153 0.393 0.080 0.548 0.542 0.125 国木田花丸 0.027 0.022 0.067 0.000 0.000 0.010 0.000 0.044 0.525 0.563 0.121 0.304 0.196 黒澤ルビィ 0.055 0.003 0.011 0.000 0.000 0.000 0.000 0.164 0.013 0.030 0.204 0.008 0.110 松浦果南 0.047 0.044 0.720 0.033 0.885 0.977 0.935 0.297 0.000 0.187 0.057 0.005 0.233 黒澤ダイヤ 0.002 0.000 0.002 0.008 0.013 0.001 0.001 0.047 0.011 0.001 0.033 0.000 0.076 小原鞠莉 0.859 0.640 0.000 0.028 0.102 0.006 0.065 0.276 0.000 0.109 0.001 0.136 0.227
さて、多項分布でモデルを組んだが、別の方法もやってみる。いま、9人13話のセリフ回数をプロットすると、右に裾が長い。バツは中央値である。単位時間にセリフを発する確率を考えるとポアソン分布だが、裾が長いときに過分散を考えるために負の二項分布を用いることがある。
可視化で理解する「負の二項分布」 - ほくそ笑む
rstan での負の二項分布は、0 以上の実数 をパラメータに取り、平均はとなる。これにより、1話における平均セリフ回数をモデル化し、同様にgenerated quantities で最下位回数を比較する。
こちらのモデルでは話数によって は変えていない。
バーンイン 1000、サンプリング 2000、チェイン 3 で40秒で推定できる。収束の はすべて1 だった。
# model02 data{ int<lower=0> N; int<lower=0> V; int<lower=0, upper=1000> serif[V, N]; } parameters{ vector<lower=0, upper=1000>[N] alpha; # 負の二項分布の平均 vector<lower=0, upper=1000>[N] beta; # 負の二項分布の分散 real<lower=0, upper=1000> lambda; } model{ for(v in 1:V){ sum(serif[v,]) ~ poisson(lambda); serif[v,] ~ neg_binomial(alpha, beta); } } generated quantities{ int s[V, N]; int<lower=10> s_all[V]; for(v in 1:V){ s_all[v] <- poisson_rng(lambda); for(n in 1:N){ s[v, n] <- neg_binomial_rng(alpha[n], beta[n]); } } }
モデル1 よりも果南が最下位になる確率は小さくなった。それでも50% はあるが…
高海千歌 桜内梨子 渡辺曜 津島善子 国木田花丸 黒澤ルビィ 松浦果南 黒澤ダイヤ 小原鞠莉 0.000 0.000 0.000 0.148 0.042 0.004 0.549 0.006 0.251
各話ごとでは、鞠莉、善子、花丸が最下位になる確率が高くなり、果南が最下位になるはずだった確率が分散しているようである。このモデルでは過分散があるため、梨子や曜ですら、1% 以下だが偶然最下位になりうる。
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] 高海千歌 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 桜内梨子 0.003 0.005 0.005 0.003 0.006 0.003 0.004 0.003 0.004 0.004 0.003 0.003 0.003 渡辺曜 0.004 0.006 0.005 0.005 0.004 0.006 0.005 0.003 0.005 0.004 0.006 0.004 0.005 津島善子 0.197 0.202 0.197 0.204 0.202 0.209 0.203 0.204 0.217 0.224 0.192 0.213 0.217 国木田花丸 0.089 0.081 0.089 0.087 0.084 0.084 0.082 0.086 0.081 0.080 0.083 0.076 0.088 黒澤ルビィ 0.055 0.048 0.045 0.046 0.049 0.049 0.052 0.049 0.045 0.041 0.051 0.046 0.047 松浦果南 0.414 0.413 0.415 0.412 0.420 0.421 0.417 0.426 0.424 0.405 0.434 0.413 0.402 黒澤ダイヤ 0.016 0.017 0.011 0.015 0.014 0.015 0.016 0.013 0.021 0.016 0.012 0.015 0.019 小原鞠莉 0.222 0.227 0.234 0.227 0.221 0.213 0.221 0.215 0.204 0.226 0.219 0.230 0.218
rstan でサンプリングされた分布に、9人の13話分のセリフ回数をpoints した。それっぽくなっているような気がする。
果南がもっとも平均 が小さいが、アニメを見なおしていて思ったのが、意外と善子もセリフは少ないのである。キャラ付けでインパクトが大きかったのである。
曜はメイン回があったので、多くしゃべっているような気がしていたのだが、梨子のほうが実は多い傾向だった。
千歌は圧倒的主人公感。
というわけで、果南のセリフが少なかったかというと、有意に少ないというわけではないが、少ないのは少ない、という結果である。
セリフ回数でやっているので、本当ならばすべてのセリフをテキストに起こして、文字数やセリフを言っている時間でやりたかったのだが、11話から見始めて最初の5分くらいで絶望感しかなかったので諦めた。
友利奈緒全セリフデータベースというものがあるので、誰か熱狂的なサンシャインファンがテキストに起こしてないかと期待したけど、なかった。
実際には果南のセリフが0 になる回数を推定すべきだったんじゃね? と思ってセリフ回数が0 になる確率をやっておいたが、モデル1 では3, 5, 7話で50% 程度0になる確率だった。モデル2 では各話で が一定なので、常に12% 程度の確率でセリフ回数が0 になるようだった。
library(rstan) rstan_options(auto_write = TRUE) options(mc.cores = parallel::detectCores()) dat <- t(as.matrix(read.delim("serifu.txt", row=1))) cols <- c("orange", "pink", "skyblue", "grey", "yellow", "deeppink2", "lightseagreen", "red", "purple") matplot(dat, col=cols, type="l", lwd=3, lty=1, xlab="話数", ylab="セリフ回数") legend("topright", legend=colnames(dat), col=cols, lty=1, lwd=3) barplot(t(dat), col=cols) par(mar=c(5, 4, 4, 4)) y <- t(dat/rowSums(dat)) barplot(y, col=cols, xlab="話数", ylab="全セリフに占める割合") p <- par()$usr text(p[2]-0.7, cumsum(y[,ncol(y)])-y[,ncol(y)]/2, rownames(y), pos=4, xpd=TRUE, col=cols, font=2) par(mar=c(5, 5.5, 4, 2)) plot(c(dat), rep(1:ncol(dat), each=nrow(dat)), pch=16, col=rep(cols, each=nrow(dat)), xlab="各話ごとのセリフ回数", ylab="", yaxt="n") axis(2, at=1:ncol(dat), colnames(dat), las=1) points(apply(dat, 2, median), 1:ncol(dat), pch=4, cex=1.5) # rstan の準備をしていく standata <- list(N=ncol(dat), V=nrow(dat), serif=dat) stanmodel <- stan_model(model_code=model01) # model02 もやる fit <- sampling(stanmodel, data=standata, chains=3, warmup=1000, iter=2000, seed=1234) ex <- extract(fit) # 松浦果南が最下位のセリフ回数か調べる saikai <- colnames(dat)[apply(mapply(function(z) colSums(ex$s[z,,]), 1:dim(ex$s)[1]), 2, order)[1,]] table(factor(saikai, colnames(dat)))/dim(ex$s)[1] res <- mapply(function(z) table(factor(colnames(dat)[apply(ex$s[,z,], 1, order)[1,]], colnames(dat))), 1:nrow(dat))/dim(ex$s)[1] # 負の二項分布モデル y <- ex$alpha/ex$beta colnames(y) <- colnames(dat) par(mar=c(5, 5.5, 4, 2)) boxplot(y, col=cols, horizontal=TRUE, xlab="平均セリフ回数", main="負の二項分布モデル", las=1, pch=16) points(c(dat), rep(1:ncol(dat), each=nrow(dat))+0.2, pch=16, col=rep(cols, each=nrow(dat)))