空飛ぶチンアナゴの統計解析日記

統計解析を嗜むチンアナゴのメモ帳です

DPCの本

DPCのデータを解析するにあたって

https://amzn.to/3FI7Dot


基礎から読み解くDPC 第3版―実践的に活用するために | 松田 晋哉 |本 | 通販 - Amazon.co.jp
は必須みたいな風潮がありますが、これ以外になんか本あったっけか? と探しているも見つからず。
なかなかDPCのデータを使って解析するのも難儀だなぁと思いながら見ている。

STATAの関数

STATAの関数の定義についてようやく理解できたのでまとめてみる。
もちろん、公式のドキュメントをみれば一発なんけど、チートシートがないのでどこから手をつけていいのかマジでわからんかったのよね。

関数を定義する

STATAで「Hello World!」を表示させる関数を書くと

capture program drop hello_world
program define hello_world
    display "Hello World!"
end

のようになる。
最初の

capture program drop hello_world

は一種のお約束のようなものである。
重複した名前の関数が既にあった場合、後から同じ名前の関数を定義できないことから、事前にこれから定義する関数と同じ名前の関数を消去している。
もちろん、同名の関数が事前にない場合も多いので、冒頭に「capture*1」とつけて同名の関数が事前になくても次の処理に進むようにしている。
次の

program define hello_world

pythonで言う所の

def hello_world():

の行に相当する。
ただし、pythonのようにインテンドでコードブロックの区切りを決めているわけではないので、関数のおしまいには必ず、

end

と一文加わることになる。

display "Hello World!"

については特に"Hello World!"と表示するというだけなので細かい説明は省略。
関数内でやらせたいことは

program define hello_world

end

の間に記述してあげればOKだ。

パラメータの設定

基本的な話

例えば、"Hello, ◯◯!"と任意の人の名前を表示したい場合を考えてみよう。
STATAのパラメータはprogramを定義した時に自動的に1番から定義してくれる*2
なので、

capture program drop hello_name
program define hello_name
    display "Hello, `1'!"
end

と書くことができる。
`1'の所には最初の引数が入力されるので

hello_name "David"

とするだけで勝手に"Hello, David!"と出力される。

そうはいっても数字では分かりにくい

とはいえ何個もパラメータがある場合、数字だけではどの数字が何のパラメータなのかは非常に分かりづらい。
で、あればパラメータに名前をつけてしまえばいい。
するとさっきのプログラムは

capture program drop hello_name
program define hello_name
    args name

    display "Hello, `name'!"
end

と改変することができる。
argsで最初のパラメータに「name」と名前をつけてあげることで、`1'は`name'としても読み込むことができるわけだ。

STATAの標準的なプログラムのように操作したい

STATAの標準的なプログラムの構文は

[byvarlist:]command[varlist][=exp][ifexp][inrange][weight][,options]

であるわけだが*3、先ほどのようにargsでパラメータを決めた場合、このような普通のsyntaxは使えない。
ではどのようにするのかというとsyntaxで設定する。

設定例

実例としてロジスティック回帰分析をこなって推定結果を保存するプログラムを書きたい。
1. ifで条件を絞る場合とそうでない場合
2. ifで絞るものを変える場合
この二つに対応できることが条件となる。
で、ついでに事後推定の結果も適当な名前で保存したい。
このようなプログラムは

capture program drop logistic_and_store
program define logistic_and_store
    syntax anything [if], STOre(string)

    logistic `anything' `if'
    estimates store `store'
end

と書くことができる。
syntaxの所は今回のif節のようにあってもなくてもいい場合は[]で囲うようにする。
また、オプションについては最低限必要な所を大文字で記載することにより、実際にコードを実行する際に大文字の所を小文字で記載することでコードを省略することができる。この際注意してほしいのは実際にオプションとして使用する変数はコード内ではすべて小文字で記載する必要があるということだ。ちなみに今回のstoreは変数名なので()内にデータの属性としてstringを指定している。
実際に

sysuse nlsw88.dta
* 単純にサンプルのデータセットを呼び出している

capture program drop logistic_and_store
program define logistic_and_store
    syntax anything [if], STOre(string)

    logistic `anything' `if'
    estimates store `store'
end

logistic_and_store never_married race age, sto(test)

でロジスティック回帰分析の結果が表示され、事後推定の結果が_est_testとして記録されているはずだ。
結構、設定が色々細かくできるので
https://www.stata.com/manuals13/psyntax.pdf
を読んでほしい。
使いこなせるとすごく便利。

まとめ

STATAのprogrammingにはargを指定する場合とsyntaxをしてする方法の2種類がある。
どちらの方法も良し悪しがあるので適時使い分けるといいだろう。

*1:https://www.stata.com/manuals/pcapture.pdf

*2:本当は0もあるが特殊なものなので、今回の説明からは省略。STATAは基本的に1が起点とだけ覚えてください

*3:https://www.stata.com/manuals/u11.pdf#u11Languagesyntax

Q-GISのファイルのパス

pythonスクリプトを書いてcsvのテーブルを読み込ませる時、ドキュメント*1に従って記載すると

csv_path = "file:///作業フォルダーまでのパス/csv_datas/なんかいっぱい入ったテーブル.csv?なんかオプション"
csv_table = QgsVectorLayer(csv_path, "レイヤーの名前", "delimitedtext")

QgsProject.instance().addMapLayer(csv_table)

とのようになる。
で、実際にこのようにコードを記載した時に読み込めないことがあってすごく困った。
ちなみにcsvじゃないshpファイルを読み込む時は

chr_dir = os.getcwd()
file_path = f"{chr_dir}/shp_files/シェープファイル.shp"
shp_file = QgsVectorLayer(file_path, "なんかのマップ", "ogr")

QgsProject.instance().addMapLayer(shp_file)

で問題なく読めたりする。

原因と対策

【原因】ファイルのパスに全角文字が含まれている

これがあるとまず読み込めない。2020年代にもなってまだこんな話があるのかと思ったけど、実際今回の原因がこれだったので仕方ない。
古い時代のQ-GISでは「パスに半角英数字以外が入るとエラーになるので、保存先とファイル名に2バイト文字(日本語とか)が入らないようにしてください*2」と記載があるとはいえ、現在の3系統では概ね「レイヤ>レイヤを追加>CSVテキストレイヤを追加」から選択すれば読み込みができるので、こういう事態が未だに起きることは想定外であった。読み込めなくてもエラーで止まらないし。

【対策】ファイルのパスに関わるところに全角文字を入れない

基本的な方法だけど大事。
ファイル名やフォルダー名は極力半角で名前をつける。
それだけで割と回避できる。

【対策】全角文字のところをバイト列に変換する

実際、「レイヤ>レイヤを追加>CSVテキストレイヤを追加」から読み込んでみると全角文字のところはすべてバイナリー文字列に変換されている。
ということはencode()でバイト列に変換すればと……思うのだけけどこれが上手くいかない。
フォルダーであれば一旦フォルダー内にある何かのファイルを1つ読み込ませて「右クリックプロパティー>プロバイダからの情報>パス」をコピペして改変とかできるけど、複数あるファイルだとなかなかに大変よなとは思う。

結論

Q-GISに使うファイルとパスは2バイト文字は使わない(特にpythonでコードを書く人は)。
なんとも古典的な方法だけど、これ以外あまりうまくいかなさそうなので仕方ないね……。

f文字列って便利ですね

何を今更と言われそうだけど、f文字列の便利さにあらためて感動している。

今更のf文字列

docs.python.org
使い方は単純で文字列の頭にfをつけて挿入したい変数を{}で囲む。

name = "Taro"
print(f"Hello, {name}")

という感じ。

これが動的に変数を作るときに便利

今までname1〜10までの変数を作ってそれに名前を一つずつ入れる場合*1、exec関数の中身を文字列を結合してコードを書くと

name_list = ['Taro', 'Jiro', 'Saburo', 'Shiro', 'Goro', 'Rokuro', 'Nanaro', 'Hachiro', 'Kuro', 'Juro']
for num in range(10):
    exec("name" + str(num+1) + "=" + "name_list[num]")
    exec("print('name" + str(num+1)+  ":', " + "name" + str(num+1) + ")")

というめんどくさいコードになる
これをf文字列を使うと

name_list = ['Taro', 'Jiro', 'Saburo', 'Shiro', 'Goro', 'Rokuro', 'Nanaro', 'Hachiro', 'Kuro', 'Juro']
for num in range(10):
    exec(f"name{num+1} = name_list[num]")
    exec(f"print('name{num+1}:', name{num+1})")

とさっきのものよりは格段に読みやすくなる。

こんなに便利ならもっと早く使っておけばよかったと軽く後悔しているw

おまけ:辞書でやると

name_array = {}
for num in range(10):
    name_array[f'name{num+1}'] = name_list[num]
    print(f'name{num+1}: ', name_array[f'name{num+1}'])

こんな感じです。
正直変数を10個作るよりかは辞書に格納した方が管理も楽ですね。
辞書の中身は単一の値以外にもリスト、辞書、データフレーム等色々使えるので、とても便利です。

*1:それ辞書でやれよというツッコミは今日はナシで

CS50のpython版

www.youtube.com
土曜日にやっていたCS50のpython版の講義がもう既にアップされていました。
基本的に、Malan先生の英語はとても聞き取りやすいのでわかりやすいと思います。


これを無料で配信しているから大したものです。

コードガールコレクションで遊んでみたよ

paiza.jp
paiza(https://paiza.jp/)さんのコードガールコレクションで遊んでみたので感想を。
ゲームとしてはタイピングオブザデッドみたいなタイプゲーでひたすらコードをタイプしていく感じです。
難易度としてはそんなに難しくないので気軽に遊べます。

難点としては

  1. コードをスクラッチで書くわけではないため、コードが書けるようにはならない
  2. 速度を出すことを重視するクセがつくので、時間をかけてでも綺麗なコードが書けるようにはならない

の2点が挙げられます。
特に後者が割と曲者だと思います。

プログラミングの基本的な要素を体に覚えさせるには最適ですが、それ以外にはそこまで役に立つゲームではないかなーと思います。
タイプゲーム感覚で暇つぶしに遊ぶにはちょうどいいかなと思います。