2018年8月30日 (木)
Bail, C.A., et al. (2018) Exposure to opposing views on social media can increase plitical polarization. PNAS, Aug 2018.
一昨日電子版が出て、メディアに取り上げられ話題になっているらしき論文。ほんとは仕事や学会発表やセミナーでいまそれどころじゃないんだけど、計算の待ち時間にイライラしながらめくった。ううむ、まさに現実逃避である。
いわく。
USにおける政治的極化は深刻な状態にある。その原因としては、「エコー・チェンバー」、つまり対立する政治的見解への接触が限定されていることによって生じる既存信念の強化が挙げられることが多い[ここで以下をreferしている: Bakshy,Messing,&Ademic(2015 Sci.), キャス・サンスティーン(2001書籍)、King,Schneer,&White(2017 Sci.), Beryy & Sobieraj(2013書籍), Prior(2013 Ann.Rev.Polit.Sci.)]。
エコー・チェンバーと極化という問題への懸念はソーシャルメディアによってさらに増している。しかし、SNSが政治的意見を形成しているのか(あるいはその逆か)を調べるのはすごくむずかしい。
というわけで、Twitterで大規模フィールド実験をやりました。仮説は3つ、ちゃんと事前登録してまっせ。
- 選択的接触を妨害すれば政治的極化は軽減される。この仮説、対人接触なら研究があるんだけど(集団間接触とステレオタイプの研究)、SNSでやるところが新しい。
- 異なる政治的見解に接するとバックファイヤが起きてかえって極化する。自分の態度と対立するメッセージのせいで既存信念への関与が高まるから。これは比較的新しい主張である[といいつつ、Load,Ross,&Lepper(1979JPSP)というのを引用しているぞ。Lepperって内発的動機づけのLepperかな?]。
- リベラル層より保守層でバックファイヤが起きやすい。保守は伝統を重視しリベラルは変化と多様性を重視するから。これも研究がいろいろあるけど、大人数でSNSでってのはない。
それでは調査デザインです。[正直、こういう話はデザインが一番面白い]
- 2017年10月から、Twitterユーザに短い調査をかけた。政治的態度、SNS利用、メディア接触など。TwitterのIDも集めた。専門の調査会社に依頼した[どこだろう?]。
- 一週間後、民主党支持者(901名)と共和党支持者(751名)を、{政党支持、Twitter利用頻度、現在の出来事への関心の程度}で層別し、処置群と統制群にランダム割付。で、処置群には11ドル払ってボットをフォローさせた。ちゃんと読ませるために、毎週調査を掛け、このボットが一日に二回ツイートしてくる動物の写真がなんだったかを訊いた[はっはっは]。これが1ヶ月間。
- 最後にまた金払って、最初のと同じ調査。
さて、このボットは一日24件をリツイートする。対象者には説明しないんだけど、実は、共和党支持者に与えられるボットはリベラルなアカウントのツイートだけをリツイートする嫌な奴であり、民主党支持者に与えられるボットは保守アカウントのツイートだけをリツイートする嫌な奴なのである。[この仕組みを作る話も面白くて、有名人のアカウントから出発してフォロー関係のネットワークをつくり、近接行列を対応分析したとかなんとか... 本筋じゃないのでパスするけど、いずれ気が向いたらappendixを読んでみよう]
自分の身になって想像してみると、なんとなく結果は読まなくてもわかるような気もするのだが... お待たせしました、結果でございます。
処置群のcomplianceにばらつきがあるので、分析がかなりややこしくなっているんだけど、そこは無視して主旨だけメモしておくと...共和党支持者は処置によってより保守的になった。民主党支持者はそうでもなかった。
考察...の前に、まずは本研究の限界について。
- USのTwitterユーザでの実験であり、USの全人口に一般化できるかわからないし、他の国でどうかもわからない。
- どっちも支持してない人は調べなかったし
- Twitter利用頻度が低い人も調べなかった。
- ツイートを読めと金払っているのも不自然。
- なによりも、共和党支持者でバックファイアが起きたメカニズムがわからない。
- ホーソン効果じゃないかというご指摘もあろう[←これはAppendixで反証を出しているそうだ]。
- ボットのリツイートの中身じゃなくて、政治的ツイートであることそれ自体が効いていたのかもしれないし[←なるほど]、
- 話題が女性問題とかマイノリティ問題であることが効いていたのかもしれない。
- リツイートしたアカウントも両陣営のエリートに偏っていた。
はいはい、いろいろ限界はありますですよ。
考察。
本研究の意義: (1)対立する政治的見解に接触させても、政治的極化は緩和されるどころか、バックファイヤが起きることが示された。(2)手法的イノベーション。
リベラルのみなさんは、よくSNSを通じて幅広い政治的見解を人々に提示しようなど考えるけど、そんなのろくなことないですよ。政治的分断に橋を掛けるためには、他の工夫をしなければ。云々。
... はっはっは。なかなか面白い論文であった。
なにより、手法が面白いっすね。「対象者に金を渡してツイッターを毎日チェックさせましょう」などという、私が会議室で話したら馬鹿にされちゃいそうなアイデアを、実行に移しているところが楽しい。なぜ処置群をもうひとつ作らなかったかなあと思うけど(「リベラルと保守の両方の政治的意見をリツイートするボット」を与える群を作っておけば、結果の解釈がもう少し絞り込めただろう)、限られた予算の下での実験としては、このデザインで正しいのであろう。
著者らも考察の手前で強調しているけど、この結果をどこまで一般化できるのかは怪しいところである。
この話、twitterの特性と深く関わっていて、実は事前の政治的態度とメディアとの間で適性処遇交互作用みたいなことが起きているんだけど、twitterだけ調べてるから「保守派は対立意見に触れると極化する」という風に見えちゃうのかもしれないな、とも思う。たとえば、自宅の隣人がリベラルだったら保守のおっさんは歩み寄るけど、隣人が保守だったらリベラルのおっさんは世を憂いてよりかたくなになる、とか? そんなこたあないか。
論文:その他 - 読了:Bail, et al. (2018) Twitterでリベラルなツイートを読むと保守派は少しは歩み寄るか? →それどころかもっと保守的になる
2018年8月12日 (日)
GLMM(一般化線形混合モデル)のRパッケージMCMCglmmというのがあるんだけど、その使い方があまりにヤヤコシイ(少なくとも私にとっては)。必要になるたびに資料をめくり、用事が終わるたびにすぐに忘れてしまう、という繰り返しで、流石にうんざりしてきたので、まとめてメモをとることにする。
参照した資料は以下の通り。
- パッケージのマニュアル。
- パッケージのビニエット。たぶん、前に読んだJSS論文と同じだと思う。
- 著者によるCourse Notes。これも昨年ひととおり目を通したのだけれど、そのときからずいぶん加筆されているような気がする。
- 著者による、2010年のチュートリアル。この資料の位置づけはよくわからない。Course Notesの旧版かと思ったのだが、ここにしか載っていない情報もあるようだ。
概念
最初に基本的な概念の説明から。
MCMCglmmでは、まず反応変数$y_i$の確率密度を
$f_i (y_i | l_i)$
とする。そういわれるとわかりにくいが、たとえばポアソン分布、ログリンクであれば、ポアソン密度関数を$f_P$として
$f_P(y_i | \exp(l_i))$
である。リンク関数で変換する前の変数$l$を潜在変数と呼ぶ。
潜在変数のベクトルを$\mathbf{l}$とする。これは個体レベルの話じゃなくて、たとえ単純なガウシアン回帰でも、100人いたら長さ100である。さらに反応変数が複数あったり、(反応変数が単一でも)潜在変数が複数あったりする場合、それを全部縦に積む。だから$\mathbf{l}$はすごく長い。
固定効果を持つ説明変数の計画行列を$\mathbf{X}$, ランダム効果を持つ説明変数の計画行列を$Z$として、線型モデル
$\mathbf{l} = \mathbf{X \beta} + \mathbf{Z u} + \mathbf{e}$
を考える。潜在変数に残差項$\mathbf{e}$が入っている点にご注目。これは測定誤差ではない。ポアソン・モデルであれば、この項でover-dispersionを説明することになる。
MCMCglmmは、係数はすべてMVNに従うと考え
$\mathbf{u} \sim N(\mathbf{0},\mathbf{G})$
$\mathbf{e} \sim N(\mathbf{0}, \mathbf{R})$
とする(これはデータ生成メカニズム)。$\mathbf{G}, \mathbf{R}$は巨大な正方行列である。なお、事前分布は逆ウィシャート分布である模様。
さらに、MCMCglmmはベイジアン・アプローチに立つので、固定効果の係数$\mathbf{\beta}$でさえ確率変数であり、
$\mathbf{\beta} \sim N(\beta_0, \mathbf{B})$
だと考える(これは事前分布)。
実行例
MCMCglmmパッケージ所収のサンプルデータBTdataには、Blue tit(アオガラ)という鳥についてのデータがはいっている。828行, 行はひなの個体を表す。変数は:
- tursus: 量的変数。ひなの体長。平均0, 分散1に標準化済み。
- back: 量的変数。ひなの色。平均0, 分散1に標準化済み。
- animal: 因子。ひなの個体ID。828水準。
- dam: 因子。母鳥の個体ID。106水準。
- fosternest: 因子。巣のID。104水準。なんでも、アオガラのひなが孵化したのち、およそ半分くらいを他の巣に移したのだそうだ。つまり巣と巣のあいだでひなを交換し、巣のなかに血縁のない子とある子が混じるようにしたらしい。かわいそうになあ。
- hatchdate: 量的変数。その巣で最初の卵が孵化した日。平均0, 分散1に標準化済み。
- sex: 因子。ひなの性別。3水準(オス、メス、不明)。
さらに、BTpedには鳥の親子関係がはいっている。1040行、行は個体を表す。変数は:
- animal: 個体ID。1040水準。
- dam: 母鳥の個体ID。106水準、欠損あり。
- sire: 父鳥の個体ID。106水準、欠損あり。
サンプルコード。わかりやすいようにすべての引数を書いてみる。
m <- MCMCglmm(
fixed = cbind(tarsus, back) ~ trait:sex + trait:hatchdate -1,
random = ~ us(trait):animal + us(trait):fosternest,
rcov = ~ us(trait):units,
family = c("gaussian", "gaussian"),
mev = NULL,
data = BTdata,
start = NULL,
prior = list(
R = list(V = diag(2)/3, n=2),
G = list(
G1 = list(V = diag(2)/3, n=2),
G2 = list(V = diag(2)/3, n=2)
),
),
tune = NULL,
pedigree = BTped,
nodes = "ALL",
scale = TRUE,
nitt = 60000,
thin = 25,
burnin = 10000,
pr = FALSE,
pl = FALSE,
verbose = TRUE,
DIC = TRUE,
singular.ok = FALSE,
saveX = TRUE,
saveZ = TRUE,
saveXL = TRUE,
slice = FALSE,
ginverse = NULL,
trunc = FALSE
)
この実行例に沿って、主な引数の指定の仕方をメモしていく。
fixed引数
fixed引数で固定効果を指定する。
fixed = cbind(tarsus, back) ~ trait:sex + trait:hatchdate -1
fixed引数はformulaをとる。formulaの左辺には目的変数を書く。この例のように目的変数が複数ある場合にはcbind()でつなぐ。
ややこしいのはformulaの右辺である。
上の例では目的変数が2個ある。MCMCglmmは目的変数の2 x n行列を、(値, 目的変数名, 行番号)の3列からなる2n x 3行列に展開する(reshape2パッケージでいうmelt(), dplyrパッケージでいうgather()ね)。で、目的変数名の列に trait, 行番号の列に unitsという名前を付けるのである。そうです、traitというのは予約語なのです! 勘弁してくださいな、もっと予約語っぽい名前にしてくださいよ... _TRAIT_ とかさ...。
上の例では、目的変数tarsusとbackそれぞれについて、固定効果を持つ説明変数としてsexとhatchdateを指定したい。つまり、素直に書けば
tarsus ~ sex + hatchdate
back ~ sex + hatchdate
である。もちろん係数はtarsusとbackで異なると考えたい。
tarsusとbackを縦に積んだ、長さ2 x nの反応変数ベクトル y についてモデルを組むことを考えよう。
y ~ sex + hatchdate
とすると、係数がtarsusとbackで共通になってしまう。だから、反応変数名とsexの交互作用項 trait:sexと、反応変数名とhatchdateの交互作用項 trait:hatchdate を投入して
y ~ trait:sex + trait:hatchdate
と書かないといけない... という理屈である。
最後に -1 として切片項を除外しているのは、そうしないとbackに対するパラメータ推定値がtarsusとの対比になってしまうからである。
この理屈もしばらく腑におちなかったのだが、上で得たオブジェクトm1について
View(as.matrix(m1$X))
を観察してようやく意味がわかった(Xは固定効果の計画行列だが、疎行列のdgCMatrixクラスになっているのでas.matrix()しないとわかりにくい)。計画行列は828x2行、8列になる。列の内訳は、まずsexのダミー行列、2(traitの水準数)x3(sexの水準数)=6列。そして、hatchdateの値を格納した2列である(うち828行は左列のみ、残り828行は右列のみ値を持ち、残りは0で埋めてある)。
仮に
fixed = cbind(tarsus, back) ~ trait:sex + trait:hatchdate
とすると、計画行列にはすべての行で1を持つ切片列が加わり、かわりにダミー行列の右端の一列(trait=back:sex=UNK)が削除される。そ、それはわかりにくいわ...
いまこのメモを書いていてふと気になったんだけど、仮に説明変数がhatchdateだけだったらどうするんだろう。hatchdateはダミー行列ではないので、
fixed = cbind(tarsus, back) ~ trait:hatchdate - 1,
だと切片が消えてしまう。
fixed = cbind(tarsus, back) ~ trait:hatchdate + trait - 1,
とかかな? こんど試してみよう。
random引数
いよいよ本丸である。random引数でランダム効果を指定する。はっきりいってすごくわかりにくいので、深呼吸するように。
random = ~ us(trait):animal + us(trait):fosternest
random引数はformulaをとる。その書式は
random = ~ 分散関数(formula):リンク関数(ランダム項) + ...
である。おそらくは左辺はいらないのだと思う。分散関数、formula, リンク関数は省略できる。
まずは、いくつかの例からみていこう。
その1, ランダム項だけ指定する場合。
random = ~ fosternest
もし目的変数がtarsusしかなかったらこれでよい。tarsusに対するfosternestのランダム効果の分散$\sigma^2_f$を推定することになる。$\mathbf{u}$は長さ104のベクトルとなり、$\mathbf{R}$は$104 \times 104$の対角行列で、対角要素はすべて$\sigma^2_f$となる。
しかし、目的変数がふたつあるときは、これではいけない。なぜか。この指定だと、tarsusに対するfosternestのランダム効果の分散は$\sigma^2_f$だ、backに対するランダム効果の分散も$\sigma^2_f$だ、そして「tarsusに対するfosternestのランダム効果とbackに対するfosternestのランダム効果の共分散」も$\sigma^2_f$だ、と推定することになるのである。
なにそれ!わけがわからないよ!いやー、そうなる理由がぜんぜん腑に落ちない...
その2, formulaを付け加える。
random = ~ trait:fosternest
目的変数が複数あるときは、この式が基本である。
いま、$\mathbf{u}$は長さ828x2の長ーいベクトルである。ランダム項としてfosternestを指定したので、計画行列$\mathbf{Z}$は828x2行、104x2列の行列となる。$\mathbf{G}$は828x2行、828x2列の壮大な正方行列である。さて、この$\mathbf{G}$から、ある特定の巣に関わる行と列だけを取り出すと、2x2の正方行列になる。行と列はtraitである。
random = ~ trait:fosternestとは、この2x2の分散共分散行列を、非対角要素は0、対角要素は同一とします、という指定である。これ、おそらくidv(trait):fosternest の略記だと思う。
その3, 分散関数を付け加える。
random = ~ idh(trait):fosternest
とすると、分散共分散行列の対角要素だけが別々に推定される。つまり、tarsusに対するfosternestのランダム効果の分散$\sigma^2_{f:tarsus}$とbackに対するfosternestのランダム効果の分散$\sigma^2_{f:back}$の2つのパラメータが推定される。非対角要素は0。
random = ~ us(trait):fosternest
とすると、非対角要素を含めた4つが別々に推定される。
この事例では、巣が体長に与える効果と体色に与える効果のあいだに共分散があると考えているので、~ us(trait): fosternest となっているわけだ。
さて、書式の詳細について。
分散関数には以下がある。
- idv: formulaのすべての要素を通じて分散を一定とする。
- idh: formulaの要素ごとに分散を推定する。
- us: formulaの要素ごとに分散を推定し、さらに共分散も推定する。
- corg: 共分散行列の対角要素を1にする。
- cogh: 共分散行列の対角要素を事前分布で指定した値に固定する。
- cors: 相関下位行列を許容する。(意味がわからない)
- ante1, ante2, ...: 1次, 2次...のante-dependence 構造。次数の前にcをつけると(例, antec2)、次数の間で回帰係数が同じになり、次数の後にvをつけると(antec2v)、イノベーション分散がすべて等しくなる。 (ここも意味がわからない)
formulaは、上の例ではtraitになっているが、量的変数を指定しても良い。たとえば
random = ~ us(1+age):individual
とすると、切片とageのスロープがindividualごとにランダムに決まることになる。
random = ~ us(1+poly(age,2)):individual
もアリである。うーむ、ややこしい。説明の流れのせいでわかりにくくなっているが、formulaでは「ある個体における計画行列」を指定し、ランダム項には個体を表す変数を指定する、という理屈であろう。
リンク関数には次の2種類がある。いずれも、複数のランダム項を+でつないで記述する。
- mm: マルチメンバーシップモデルとなる。
- str: ランダム効果のあいだの共分散が推定される。
なお、上の例でus(trait):animalというのをいれているのは、pedigree引数を指定しているからなのだそうである。ここはよくわからない。
rcov引数
rcov引数で$\mathbf{R}$を指定する。
rcov = ~ us(trait):units
書式はrandom引数と同じだが、項はひとつしかとれない。
おそらく、多変量なら ~ us(traits):units とするのが基本であろう。単変量なら ~ units かな?
family引数
family引数で分布を指定する。
family = c("gaussian", "gaussian")
family引数は、反応変数の数だけの文字列ベクトルをとる。指定できる分布はは以下の通り。
- gaussian
- poisson. ログリンクとなる。
- categorical. カテゴリ1が参照水準になり、(水準数-1)列の潜在変数ができる。
- multinomialJ. Jはカテゴリ数。つまり、たとえばmultinomial3だったら反応変数は3列必要。カテゴリJが参照水準になり、2列の潜在変数ができる。
- ordinal.
- threshold. これはなんだかよくわからない。マニュアルには記載があるがビニエットにはない。
- exponential.
- geometric.
- 先頭にcenをつけるとcensored変数のモデルになる。cengaussian, cenpoisson, cenexponentialがある。
- 先頭にziをつけるとゼロ過剰変数のモデルになる。zipoisson, zibinomialがある。
- ztpoisson(zero-trancated poisson), hupoisson(hurdle poisson), zapoisson(zero altered poisson).
mev引数
各データ点における測定誤差分散を表すベクトルを指定できるらしい。
この引数はメタ分析の際に使うことを想定しているらしいのだが... まてよ、これを使えば小地域推定のFay-Harriotモデルを推定できるのではなかろうか。
prior引数 もうひとつの山場、事前分布の指定である。
prior = list(
R = list(V = diag(2)/3, n=2),
G = list(
G1 = list(V = diag(2)/3, n=2),
G2 = list(V = diag(2)/3, n=2)
),
)
prior引数はリストをとる。リストには3つの要素をいれることができる。
- B. リストをとる。そのリストには、要素muとVをいれる。それぞれ$\mathbf{\beta_0}$, $\mathbf{B}$を意味する。デフォルトでは、mu=0, V=I*1e+10である(Iは単位行列)。
- R. $\mathbf{R}$の指定である。リストをとる。その書式は後述する。
- G: $\mathbf{G}$の指定である。リストをとる。その各要素はランダム項に対応する。上の例ではG1, G2と名前を付けているが、そうしないといけないのかどうかはよくわからない。各要素はリストをとる。その書式は後述する。
さて、上述のR、ならびにGの各要素は、以下の要素を持つリストである。
- V: 期待される共分散行列。
- nu: 逆ウィシャート分布の自由度。
- alpha.mu, alpha.V: 正直、これは意味が良く理解できない。通常は指定しないのだそうである。
- fix: 指定しないと分散パラメータを推定する。1にすると固定。ほかに、1でない整数を指定して、分散共分散行列の一部を推定するように指示できるらしいのだが、この機能も良く理解できていない。
わかりにくいので例に基づいて考えよう。
上の例では、R, G1, G2のいずれについても、Vは2x2の対角行列(対角要素は1/3)、nは2としている。Rに注目しよう。rcov引数により、体長の潜在変数における残差項の分散$\sigma^2_{e:tarsus}$、体色の潜在変数の残差項の分散$\sigma^2_{e:back}$, 体長の潜在変数における残差項と体色の潜在変数における残差項との共分散$\sigma_{e:tarsus,back}$の3つを推定することになっている。しかしここでは、Rの持つVは対角行列で、対角要素は1/3である。つまり、$\sigma_{e:tarsus,back}$の事前分布の期待値は0だと指定していることになる。
対角要素を1/3としているのは、この事例では反応変数がすでに標準化されていて分散1であり、それを3つの要素に均等に分けている、という意味合いである。
ところで、prior引数の要素R、ならびに要素Gの各要素は、デフォルトではlist(V=1, nu=0)なのだそうである。うーん、よくわからん。
- 仮に潜在変数がk個あったら、Vはk x k の正方行列でないといけないと思うんだけど、そのときMCMCglmmはV=1をV=diag(k)と解してくれるのか? それとも「正しい事前分布を指定しろ」とエラーを吐くのか? これは試してみればわかることだけど。
- k x kの共分散行列の事前分布を逆ウィシャート行列$IW(\Lambda, \nu)$で表現する場合、ふつうは$\Lambda = \mathbf{I}, \ \ \nu = k+1$とするんじゃないかと思うわけです。こうすると、得られる相関の周辺分布が一様になるのだと聞いたことがある。しかるに、MCMCglmmのデフォルトでは、$\nu$ をやたらに小さくするわけだ。なぜこうしているんだろう?
starts引数
初期値の指定。リストをとる。次の4つの要素をいれることができる。
- R: $\mathbf{R}$の初期値。行列をとるのかな?
- G: $\mathbf{G}$の初期値。リストをとる。その要素数はランダム効果の要素数と一致させる。要素はどうするんだろう... 行列のとるのかな?
- liab: 潜在変数の初期値。liabというのはliabilitiesの略らしい。意味がわからないが、きっとこのパッケージ開発者の研究分野では筋の通った命名なのだろう。なにをとるのかよくわからない(ベクトル?)。
- QUASI: 論理値をとる。TRUEだと潜在変数の初期値がheuristicallyに決められ、FALSEだとZ分布からサンプリングされるのだそうである。
... と、この辺で力尽きたので、メモはここまでにしておく。さらに難解なpedigree引数、ginverse引数には、残念ながら踏み込めなかった。またの機会に。
いやー、自分の能力を棚に上げていうけど、なんてわかりにくいんだろう。SASのproc mixedのほうがはるかにわかりやすい。参ったなあ。
雑記:データ解析 - 覚え書き:RのMCMCglmmパッケージの使用方法の難解さを目の前にして途方に暮れるの巻
2018年8月 6日 (月)
松下竜一 その仕事〈15〉砦に拠る
[a]
松下 竜一 / 河出書房新社 / 2000-01
最近読んだ本のなかでの、いや、ここ数年に読んだ本のなかでのベスト・ワン。読み終えてからも深い印象を残す、これは大変な本であった。
大分・熊本の県境、筑後川水系に建設された下筌(しもうけ)ダムの建設をめぐる反対運動、通称「蜂ノ巣城事件」を描いたノンフィクション。
ダム建設予定地の地主のひとりである室原友幸さんは、村では「大学さん」と呼ばれる孤高の隠棲者、いっちゃなんだが大変に偏屈な老人である。1957年、村の人々がダム反対運動への協力を求めに訪れたとき、この老人は問い返す。「おれがいったん反対に立てば、絶対に途中でやめん。それについてくるだけの覚悟が君たちにあるのか」その言葉の通り、この人は1970年に亡くなるまで、孤独で凄絶な抵抗を続けることになる。
室原さんは偏屈にして奇矯、お世辞にも住民運動のリーダーとはいう柄ではなく、むしろ最後の封建領主という表現がふさわしい。予定地に築いた壮大な砦「蜂ノ巣城」には村人たちが総出で立てこもるし、後には三池労組をはじめとする革新系組織が反対運動に参加するが、この人は結局のところ、たったひとりで闘っているのである。法律から地質学、電気工学にいたるまでの書籍を座敷に積み上げ、早朝から独学を続ける様子は鬼気迫る。六法全書を片手にみずから提訴を乱発、法廷での争いは十年間で約八十件に及んだという。
この物語には善人も悪人もいない。九州地方建設局の作業隊は村人たちへの怒りに燃え、砦になだれ込もうとするも中止を命じられ啜り泣く。現場の人望厚い所長は、ダム建設への情熱故に上層部から危険視され更迭される。人々はやがて展望のない闘争から離れ、砦を去って行く。反対運動にとって最大の挫折となった東京地裁の判決後、弁護士はなぜか控訴手続きを怠り敗訴を確定させる(その理由はいまも謎であるという)。その裁判長はのちに室原さんと親交を結ぶことになる。新任の所長もまた、晩年の室原さんとの信頼関係を築き、この老人に「花道」を用意すべく奔走する。様々な人々のドラマが、室原さんという孤高の老人を中心に回転していく。
国会の参考人喚問の場で、室原さんは九州地方建設局幹部の名刺を振りかざす。住所変更の前につくった名刺を、住所をペンで書き換えて渡すとは、こんな失礼なことがあるか、と激怒するのである。室原さんの怒りは誇り高さゆえの私憤であるともいえる。しかしその怒りはやがて、国家という大きな装置に抗して個人の尊厳を守り抜こうとする、普遍的な闘いへと連なっていく。
著者の松下竜一さんは、「豆腐屋の四季」で世に出た往年のノンフィクション作家として名前を覚えていたが、これほどの大作家とは知らなかった。
著者の後書きによれば、俳優の緒形拳がこの作品の映画化を熱望していたらしい。わかるなあ。