【独学】基本情報技術者試験の勉強法
基本情報技術者試験に合格したので勉強法を記録しておきます。
2020年の秋試験がコロナで中止になり、CBT方式での受験になりました。
勉強期間:4+3ヶ月(コロナで延期)
勉強時間:1日15〜30分
午前試験
- 教科書一周読んで練習問題を解く
- 過去問サイトを1日5問
- 試験前に過去問を4年分
教科書を1周してからは、ひたすら過去問サイトで勉強していました。
使い回しの問題もあるので、過去問サイトはかなりやりました。
過去問は時間の感覚を掴むのに1年分だけ解くだけでも十分だと思います。
午後試験
- 過去問を4年分
- データベース:PostgreSQL環境構築して基本構文の練習
環境構築が面倒な人はProgateなどのサイトで練習しておくだけでも有効だと思います。
プログラミング言語は普段から使っているものがあれば特に対策はいらないと思います。
Pythonは今年からの出題で過去問がありませんでしたが、なにも問題なかったです。
午後試験は読解量が多く、時間がシビアです。
過去問は数年分解いたほうが良いでしょう。
使った参考書
- 作者:角谷 一成,イエローテールコンピュータ
- 発売日: 2019/11/25
- メディア: 単行本(ソフトカバー)
令和02年【春期】 基本情報技術者 パーフェクトラーニング過去問題集
- 作者:山本 三雄
- 発売日: 2019/12/26
- メディア: 単行本(ソフトカバー)
参考書はこの2冊だけでした。
ただしどちらも過去問サイトにあるので買わなくてもOKかと。
結果
CBT方式なので受験直後にスコアが確認できます。
午後試験のスコア概要だけなぜか葉書が送られてきます。
合格発表は約1ヶ月後で、その後に合格証書が送られてきます。
得点:
- 午前:87.5
- 午後:90
試験日が延長されたこともあり、なかなか高得点。
最後に
以上、基本情報技術者試験の勉強法でした。
この記事が誰かの参考になれば幸いです。
PostgreSQL操作まとめ
よく使う操作の自分用備忘録です。
目次
データベース一覧を表示
psql -l
データベースに接続
psql データベース名
テーブル一覧
\dt
postgreSQLを終了
\q
データベースの作成
createdb データベース名
テーブルの作成
create table テーブル名 ( id INTEGER NOT NULL, name varchar NOT NULL, age INTEGER, gender INTEGER PRIMARY KEY (id) );
id,name,age,genderの4つのカラムのテーブルを作成しました。
データの更新
update テーブル名 set 列名=データ, 列名=データ,....;
where条件も指定できる。
カラムの追加
alter table テーブル名 add 列名 型名;
カラムの削除
alter table テーブル名 drop column カラム名;
カラム名の変更
alter table テーブル名 rename 元のカラム名 to 新しいカラム名;
テーブルの削除
drop table テーブル名;
レコード追加
insert into テーブル名 (カラム名, カラム名) values(値,値);
レコード削除
delete from テーブル名 where 条件;
CSVからインポート
\copy テーブル名 from CSVのパス with csv
とりあえずこれだけあればなんとかなるはず
causallibでIPWをやってみる
因果推論の勉強をはじめました。
IPW法を学んだのでPythonで実際にやってみます。
causallib
というライブラリを使ってIPW法をやってみました。
こちらのサンプルに沿って進めていきます。
実行環境
- Googl Colaboratory
- causallib (0.6.0)
- sklearn (0.22.2)
使用するデータの確認
喫煙をやめたことによる体重の変化を検証します。
import causallib import sklearn data = load_nhefs() data.X.join(data.a).join(data.y).head()
データの内容はざっくりと以下のようになっています。
- data.X : 共変量 (年齢、性別、喫煙年数、運動習慣など)
- data.a : 割当(喫煙をやめていれば1、やめていなければ0)
- data.y : 目的変数 (体重の増減(ポンド))
割当を確認します。
#割当
data.a.value_counts()
0 1163 1 403 Name: qsmk, dtype: int64
1163人は喫煙のまま、403人が禁煙したようです。
目的変数を確認します。
#目的変数
data.y.describe()
count 1566.000000 mean 2.638300 std 7.879913 min -41.280470 25% -1.478399 50% 2.603811 75% 6.689581 max 48.538386 Name: wt82_71, dtype: float64
体重は平均で2.6ポンド増加したようです。
禁煙した人としなかった人での体重の変化を比較してみます。
df = data.X.join(data.a).join(data.y) print("割当1 : ", df[df.qsmk == 1]["wt82_71"].mean()) print("割当0 : ", df[df.qsmk == 0]["wt82_71"].mean())
割当1 : 4.525078989702233 割当0 : 1.9844975347463472
禁煙した人の方が体重が増加していることがわかります。
IPW法
傾向スコアの予測
サンプルの通りに実装してみます。
#train learner = LogisticRegression(solver="liblinear") #傾向スコア予測に使うモデル ipw = IPW(learner) ipw.fit(data.X, data.a) #共変量から割当を予測 #傾向スコア probs = ipw.compute_propensity(data.X, data.a, treatment_values=1)#割当1になる確率 probs.head()
0 0.131687 1 0.168435 2 0.169179 3 0.481203 4 0.251937 Name: 1, dtype: float64
ここの値は割当1になる確率です。
重み付けの確認
傾向スコアの逆数で重み付けされていることを確認します。
ipw.compute_weights(data.X, data.a).head()
0 1.151658 1 1.202552 2 1.203629 3 1.927535 4 1.336785 dtype: float64
- 割当1のデータは 1/傾向スコア
- 割当0のデータは 1/(1 - 傾向スコア)
の値になっています。
結果を確認
#割当ごとに重みづけして結果を計算 outcomes = ipw.estimate_population_outcome(data.X, data.a, data.y) #ATE effect = ipw.estimate_effect(outcomes[1], outcomes[0]) effect
diff 3.490373 dtype: float64
禁煙によって3.4ポンド体重が増加したようです。
パラメータを変化させる
次にパラメータを変化させて実行してみます。
傾向スコアが0に近すぎると、逆数をとった時に極端に大きい値になります。
また、1に近すぎると、影響が小さくなってしまいます。
今回は傾向スコアを0.2から0.8の範囲に限定します。
learner = LogisticRegression(penalty='l1', C = 0.01, max_iter=500, solver = 'liblinear') #傾向スコアを0.2から0.8に限定 truncate_eps = 0.2 ipw = IPW(learner, truncate_eps = truncate_eps, use_stabilized=False) ipw.fit(data.X, data.a) #共変量から割当を予測
傾向スコアの確認
#傾向スコア probs = ipw.compute_propensity(data.X, data.a, treatment_values=1)#割当1になる確率 print(probs.min()) print(probs.max())
Fraction of values being truncated: 0.23499. 0.7047037999493072 0.2
ちゃんと0.2から0.8の間に収まっています。
結果の確認
#割当ごとに結果を重み付け outcomes = ipw.estimate_population_outcome(data.X, data.a, data.y) #ATE effects = ipw.estimate_effect(outcomes[1], outcomes[0], effect_types=["diff", "ratio"]) effects
Fraction of values being truncated: 0.23499. diff 3.376068 ratio 2.917687 dtype: float64
禁煙による体重の変化は3.37ポンドとなりました。
モデルの評価を行う
最後にモデルの評価をします。
evaluator = PropensityEvaluator(ipw)
results = evaluator.evaluate_simple(data.X, data.a, data.y, plots=None)
results.scores.prediction_scores
見慣れない指標について
- hinge : ヒンジ損失。(参考)
- matthews : マシューズの相関係数。-1から1。1に近いほど良い分類ができている。(参考)
- 0/1 : ???知ってる人教えてください
- brier : ブライアスコア。0から1で、0に近いほど正確であることを表す。(参考)
trainとvalidで表示されます。
from sklearn import metrics plots = ["roc_curve", "pr_curve", "weight_distribution", "calibration", "covariate_balance_love", "covariate_blance_slope"] metrics = {"roc_auc": metrics.roc_auc_score, "avg_precision": metrics.average_precision_score,} ipw = IPW(LogisticRegression(solver="liblinear")) evaluator = PropensityEvaluator(ipw) results = evaluator.evaluate_cv(data.X, data.a, data.y, plots=plots, metrics_to_evaluate=metrics)
- 左上:ROC曲線。AUCは0.7以上だと良いとされています。
- 右上:PrecisionとRecallのグラフ
- 左中:傾向スコアの分布。割当0は0に近いほど多く、割当1は1に近いほど多いと良いようです。
- 右中:キャリブレーションカーブ。横軸は傾向スコア。縦軸は割当1のデータの割合。x=yの直線に近いほど良い。
- 左下:共変量の平均差を標準誤差で割ったものの絶対値??(ここがよくわかりませんでした)
- 右下:左下と同じ。
独自データに適用してみる
予め因果効果のわかっているデータを作成し、精度を検証したいと思います。
データの作成
年齢と性別によって目的変数yが変化するデータを作成します。
割当zは 年齢と性別からシグモイド関数によって決定しました。
from numpy.random import * import matplotlib.pyplot as plt import pandas as pd import numpy as np #データ数 num_data = 10000 #性別 gender = randint(0, 2, num_data) #年齢 age = randint(20, 81, num_data) #ノイズの生成,randnは標準正規分布 e_z = randn(num_data) #シグモイド関数に入れる部分 #これで割当を決める #ここを調節することで傾向スコアが満遍なく散らばるようにする z_base = 5*gender + age - 50 + 5*e_z #シグモイド関数 def sigmoid(x): return 1/(1+np.exp(-x)) #シグモイド関数を計算 z_prob = sigmoid(0.1*z_base) #割当の配列を作成 z = np.array([]) for i in range(num_data): tmp = np.random.choice([0,1], size=1, p=[1-z_prob[i], z_prob[i]])[0]#pは選ぶ確率 z = np.append(z, tmp)#配列の更新 #目的変数のノイズを生成 e_y = randn(num_data) #目的変数を決める #ここのzの係数が因果効果 y = 5000 + 10*gender + 30*age + 60*z + 10*e_y
割当zによる目的変数の変化は60にしました。
データフレームの作成
causallibで使えるようにデータを整形します。
#データフレームの作成 df = pd.DataFrame({'gender':gender, 'age': age, 'z':z, 'y':y}) df_x = df[['gender', 'age']] df_y = df['y'] df_z = df['z'] df.head()
gender | age | z | y | |
---|---|---|---|---|
0 | 0 | 76 | 1 | 7350.62 |
1 | 1 | 60 | 0 | 6784.8 |
2 | 1 | 34 | 0 | 6036.7 |
3 | 1 | 67 | 1 | 7089.36 |
4 | 0 | 50 | 0 | 6514.17 |
このようなデータを作成しました。
IPW法を適用する
これまで通りIPW法を適用します。
truncate_epsは0.01にしました。
#train learner = LogisticRegression(solver="liblinear") ipw = IPW(learner, truncate_eps=0.01) ipw.fit(df_x, df_z) #効果の確認 outcomes = ipw.estimate_population_outcome(df_x, df_z, df_y) #(割当1の目的変数 * 1/傾向スコア) - (割当0の目的変数 * 1/(1-傾向スコア))の合計 effect = ipw.estimate_effect(outcomes[1], outcomes[0], effect_types=["diff", "ratio"]) effect
出力
Fraction of values being truncated: 0.00000. diff 65.454931 ratio 1.010065 dtype: float64
割当の有無によるyの差は65.5と予測されました。
zの係数は60にしていたので、近い値ではありますね。
なお、同じ方法でデータを作成しても、予測値はばらつきました。
(55~80くらい)
何回か実行して平均をとった方が良いのかもしれないです。
評価
最後に評価を行います。
#評価 from sklearn import metrics plots=["roc_curve", "pr_curve", "weight_distribution", "calibration", "covariate_balance_love", "covariate_balance_slope"] metrics = {"roc_auc": metrics.roc_auc_score, "avg_precision": metrics.average_precision_score,} ipw = IPW(LogisticRegression(solver="liblinear"), truncate_eps=0.01) evaluator = PropensityEvaluator(ipw) results = evaluator.evaluate_cv(df_x, df_z, df_y, plots=plots, metrics_to_evaluate=metrics)
AUCが0.85と高く、 傾向スコアは理想的な形に分布しています。
データとしてはかなり予測しやすいものだったのではないでしょうか。
最後に
以上causallibによるIPW法でした!
因果推論初心者ですが、公式のサンプルがわかりやすかったのでなんとか理解できたかと。。。
評価方法やその基準についてわかっていないことが多いので、今後勉強していこうと思います。
SQLで日付の操作
日付とか年齢とかの計算方法を忘れがちなので備忘録。
よく使う日付に関する型は以下の2つです。
- date型 : 日付まで。'YYYY-MM-DD'
- timestamp型 : 日付と時間。'YYYY-MM-DD HH:MM:SS'
今回は以下の表を操作します。
date_test表
int_date | str_date | ts_date ----------+--------------+--------------------- 20200101 | 2020年1月1日 | 2020-01-01 00:00:00 20200201 | 2020年2月1日 | 2020-02-01 00:00:00 20200301 | 2020年3月1日 | 2020-03-01 00:00:00
文字列からdate型への変更
TO_DATE()
を使います。
SELECT TO_DATE(str_date, 'YYYY年mm月dd日') FROM date_test;
実行結果
to_date ------------ 2020-01-01 2020-02-01 2020-03-01 (3 rows)
intからdate型に変更
文字列に変換してからdateに変換します。
SELECT TO_DATE(TO_CHAR(int_date, 'FM99999999'), 'YYYYmmdd') FROM date_test;
実行結果
to_date ------------ 2020-01-01 2020-02-01 2020-03-01 (3 rows)
timestampからdate型に変更
CAST(カラム名 AS DATE)
を使います。
SELECT CAST(ts_date AS DATE) FROM date_test;
実行結果
ts_date ------------ 2020-01-01 2020-02-01 2020-03-01 (3 rows)
日付に関する演算
期間内のレコードを取得
date型同士は普通に足し引きできる。
SELECT str_date FROM date_test WHERE '2020-04-01' - TO_DATE(str_date, 'YYYY年mm月dd日') < 40;
実行結果
date ------------ 2020-03-01 (1 row)
2020年4月1日から40日以内のレコードのみ取得しました。
年齢のカラムを作成してみる
2038年時点での年齢を計算しました。
SELECT ('2038-01-01' - CAST(ts_date AS DATE) )/365 as age FROM date_test;
実行結果
age ----- 18 17 17
pandasでの型変換
pandasでの型変更をよく忘れるのでメモしておきます。
datetime型に変更するのか、それ以外かで処理が変わります。
目次:
datetime以外に変更
astype()
を使います。
#dfの作成 data = np.array([[1,2,3],[4,5,6],[7,8,9]]) index = ['one', 'two', 'three'] columns = ['a','b','c'] df = pd.DataFrame(data, index=index, columns=columns)
型を変更する
#strに変更 new_df = df.astype('str') new_df.dtypes
実行結果
a object b object c object dtype: object
intからstrに変更しました。
pandasではstrはobject型と表記されます。
datetimeに変更する
datetime型に変換する時は、to_datetime()
を使います。
YYYYmmdd | YYYY/mm/dd | |
---|---|---|
0 | 20200101 | 2020/01/01 |
1 | 20200201 | 2020/02/01 |
2 | 20200301 | 2020/03/01 |
#datetimeに変換する df['YYYYmmdd'] = pd.to_datetime(df['YYYYmmdd']) df['YYYY/mm/dd'] = pd.to_datetime(df['YYYY/mm/dd']) df
実行結果
YYYYmmdd | YYYY/mm/dd | |
---|---|---|
0 | 2020-01-01 | 2020-01-01 |
1 | 2020-02-01 | 2020-02-01 |
2 | 2020-03-01 | 2020-03-01 |
基本的なフォーマットなら勝手に変更してくれます。
年月日表記
や int
から datetime にしたい時は、フォーマットを指定します。
年月日 | int | |
---|---|---|
0 | 2020年1月1日 | 20200101 |
1 | 2020年2月1日 | 20200201 |
2 | 2020年3月1日 | 20200301 |
このような表記の時は、フォーマットを指定して変換します。
#フォーマットを指定して変換 df['年月日'] = pd.to_datetime(df['年月日'], format='%Y年%m月%d日') df['int'] = pd.to_datetime(df['int'], format='%Y%m%d') df
実行結果
年月日 | int | |
---|---|---|
0 | 2020-01-01 | 2020-01-01 |
1 | 2020-02-01 | 2020-02-01 |
2 | 2020-03-01 | 2020-03-01 |
datetime型に変換できました。
フォーマットの指定方法は下記の通りです。
- %Y : 年(4桁)
- %y : 年(2桁)
- %m : 月
- %d : 日
- %H : 時
- %M : 分
- %S : 秒
以上pandasでの型変換でした。
他にも忘れがちな操作があれば書き足します。
pandasで条件指定して抽出、分岐
pandasの条件指定で表を操作します。
条件を指定する方法を忘れがちなのでメモっておきます。
目次:
今回使用するdf
data = np.array([[1,2,3],[4,5,6],[7,8,9]]) index = ['one', 'two', 'three'] columns = ['a','b','c'] df = pd.DataFrame(data, index=index, columns=columns) df
a | b | c | |
---|---|---|---|
one | 1 | 2 | 3 |
two | 4 | 5 | 6 |
three | 7 | 8 | 9 |
このdfをいじっていきます。
条件を指定して抽出
1つの列について条件を指定して抽出する時
#'a'のカラムが奇数なら抽出 df[df['a']%2 == 1]
a | b | c | |
---|---|---|---|
one | 1 | 2 | 3 |
three | 7 | 8 | 9 |
a列が奇数なら抽出。
2つ以上の条件を指定するときは&
でつなぐ。
#'a'が奇数、'c'が9以上 df[(df['a']%2 == 1) & (df['c'] >= 9)]
a | b | c | |
---|---|---|---|
three | 7 | 8 | 9 |
&
の他に|
(or)も使えます。
条件で処理を分岐させるとき
apply
を使います。
#関数を定義する def func(x): if x > 5: return 1 else: return 0 #c列にapplyする df['d'] = df['c'].apply(func) df
a | b | c | d | |
---|---|---|---|---|
one | 1 | 2 | 3 | 0 |
two | 4 | 5 | 6 | 1 |
three | 7 | 8 | 9 | 1 |
定義した関数をc列に適用します。
ラムダ式を使うと簡潔に書けます。
#c列が5以上なら1 df['frag'] = df['c'].apply(lambda x : 1 if x > 5 else 0)
a | b | c | flag | |
---|---|---|---|---|
one | 1 | 2 | 3 | 0 |
two | 4 | 5 | 6 | 1 |
three | 7 | 8 | 9 | 1 |
複数の列を条件に分岐させることもできます。
#関数定義 #2つのカラムを条件にしたいとき def func(x): if x.a * x.b < 30: return 0 else: return 1 #dfに対してapplyする df['frag2'] = df.apply(func, axis=1)
a | b | c | flag | flag2 | |
---|---|---|---|---|---|
one | 1 | 2 | 3 | 0 | 0 |
two | 4 | 5 | 6 | 1 | 0 |
three | 7 | 8 | 9 | 1 | 1 |
複数の列を条件に分岐させる時はdf自体にapplyします。
以上pandasでの条件指定方法でした。
これ以外にも忘れがちな条件指定があれば書き足していきます。
causallibでダブルロバストをやってみる
因果推論の勉強をはじめました。
ダブルロバスト法を学んだのでPythonで実際にやってみます。
今回はcausallib
というライブラリを用いて、公式のサンプルに沿って進めていきます。
実行環境
- Googl Colaboratory
- causallib (0.6.0)
- sklearn (0.22.2)
データの確認
今回は禁煙による体重の変化を推論します。
from sklearn.linear_model import LogisticRegression, LinearRegression from causallib.datasets import load_nhefs from causallib.estimation import IPW, Standardization, StratifiedStandardization from causallib.estimation import DoublyRobustVanilla, DoublyRobustIpFeature, DoublyRobustJoffe from causallib.evaluation import PropensityEvaluator, OutcomeEvaluator data = load_nhefs() data.X.join(data.a).join(data.y).head()
データの内容はざっくりと以下のようになっています。
- data.X : 共変量 (年齢、性別、喫煙年数、運動習慣など)
- data.a : 割当(喫煙をやめていれば1、やめていなければ0)
- data.y : 目的変数 (体重の増減(ポンド))
割当の内訳を確認します。
#割当
data.a.value_counts()
0 1163 1 403 Name: qsmk, dtype: int64
1163人は喫煙のまま、403人が禁煙したようです。
次に、目的変数(体重の変化)について確認します。
#目的変数
data.y.describe()
count 1566.000000 mean 2.638300 std 7.879913 min -41.280470 25% -1.478399 50% 2.603811 75% 6.689581 max 48.538386 Name: wt82_71, dtype: float64
体重は平均で2.6ポンド増加したようです。
禁煙した人としなかった人での体重の変化を比較してみます。
df = data.X.join(data.a).join(data.y) print("割当1 : ", df[df.qsmk == 1]["wt82_71"].mean()) print("割当0 : ", df[df.qsmk == 0]["wt82_71"].mean())
割当1 : 4.525078989702233 割当0 : 1.9844975347463472
単純な比較では、禁煙した人は、しなかった人よりも2.54ポンド増加していることがわかります。
ダブルロバスト
causallib
には3種類のダブルロバストがありました。
よくわからなかったのですが、公式ドキュメントを読んだ感じ以下の違いがあるようです。
- Doubly Robust Vanilla : 通常のダブルロバスト
- Doubly Robust IP-Feature : 反実仮想を求める際に、特徴量にIPWも加える。
- Doubly Robust Joffe : 反実仮想を求める際に、IPWで重み付けしている。
今回はDoubly Robust Vanillaを使用しました。
ipw = IPW(LogisticRegression(solver="liblinear"), truncate_eps = 0.05)#ロジスティック回帰でIPW std = StratifiedStandardization(LinearRegression())#反実仮想を求めるモデル dr = DoublyRobustVanilla(std, ipw)#反実仮想を求めるモデル,傾向スコアを求めるモデル dr.fit(data.X, data.a, data.y)#共変量のDF、割当のSeries、目的変数のSeries
個々人の結果を確認してみます。
#ITEの確認
ind_outcome = dr.estimate_individual_outcome(data.X, data.a)
ind_outcome.head()
qsmk 0 1 0 4.174723 7.564068 1 5.715355 10.489504 2 1.069715 8.723563 3 -3.004029 -2.249906 4 1.303085 8.072923
全体の結果を確認します。
pop_outcome = dr.estimate_population_outcome(data.X, data.a) effect = dr.estimate_effect(pop_outcome[1], pop_outcome[0]) effect
diff 3.443486
dtype: float64
禁煙によって3.4ポンド体重が増加という結果になりました。
評価
次に、モデルの評価を行います。
まず傾向スコアを求めるモデルから評価します。
#傾向スコアのモデルについての評価 prp_evaluator = PropensityEvaluator(dr.weight_model) results = prp_evaluator.evaluate_simple(data.X, data.a, data.y, plots=["roc_curve", "weight_distribution"]) results.scores.prediction_scores
上のグラフはROC曲線です。一般に0.7以上だと良いとされているようです。
下のグラフは傾向スコアの分布です。
理想的な分布は
- treatment=0:0に近いほど多い
- treatment=1:1に近いほど多い
とされていますが、それとは大きく異なっています。
傾向スコアを求めるモデルを変更するともっといい結果が出るかもしれません。
次に反実仮想を求めるモデルの評価です。
#反実仮想を求めるモデルの評価 out_evaluator = OutcomeEvaluator(dr) results = out_evaluator.evaluate_simple(data.X, data.a, data.y, plots=["common_support", "continuous_accuracy"]) results.scores
下のグラフが実測値と予測値の分布です。
最後に
非常に簡単にダブルロバストを行うことができました!
今回はサンプルを使ってみただけで、結果をどの程度信頼していいのかよくわかっていません。。。
今後、その他の手法とモデルの評価方法を勉強していこうと思います。
以上causallibによるダブルロバストでした。