yuuuuu03
@yuuuuu03

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

【python(初学者)】ロジスティック回帰の記述について

Q&A

Closed

解決したいこと

pythonでロジスティック回帰のコードを書く練習をしています。
初学者のため、練習用のコードを書き写し練習しているのですが、以下のコードでやっている意味が調べてもよくわかりません。
各lineでどのような処理をしているのか教えていただけますと大変嬉しいです。
全部で0-9列までデータがあり、8列目を目的変数、その他を説明変数をして用いたいです。
【聞きたいこと】
1).T, .tolistを使用している意味
2)np.asarrayを使用している意味
などです

データをロジスティック回帰の引数に渡せる形に整える

発生している問題

# データをロジスティック回帰の引数に渡せる形に整える
train = np.asarray([df_train[d].tolist() for d in range(1,8)]) #説明変数1-8行目
train = train.T
train_ans = np.asarray(df_train[8].tolist())
test = np.asarray([df_test[d].tolist() for d in range(1, 8)])
test = test.T
test_ans = np.asarray(df_test[8].tolist())

自分で調べたこと

.T:二次元配列の行と列を入れ替える(転置)
.tolist:dataframeやsereisを最も簡単なリスト型に変換するメソッド
0

1Answer

まず初めにロジスティック回帰でやるべきことを考えてみましょう.「データをロジスティック回帰の引数に渡せる形に整える」と書かれている通り,複数の説明変数$\boldsymbol{x}$から1つの確率(目的変数の値)$y$を出すのが目的のはずです.
式は

y = \frac{1}{1+\exp(\alpha+\boldsymbol{\beta}\cdot\boldsymbol{x})}

でしょうか.
上のコードでは,回帰式に必要な複数の説明変数$\boldsymbol{x}$と目的変数$y$を対応づけるようにするための処理を行なっています.

元のデータが示されていませんが恐らくdf_train

df_train 説明変数0 説明変数1 説明変数2 ... 説明変数7 目的変数
データ0 1 101 2 ... 3 1
データ1 2 100 4 ... 6 0
データ2 3 99 6 ... 9 1
... ... ... ... ... ... ...
データn-2 100 1 200 ... 300 1
データn-1 101 0 202 ... 303 0

みたいな感じで並んでたのではないでしょうか.値が例示されていなかったので全て適当です.
ソースコードが一部しか示されていないので分かりづらいですが,変数名にdfを用いていることから,PandasDataFrameを使われているとお察しします.

ここで, ロジスティック回帰で必要なデータの形式を振り返ってみます.複数の説明変数$\boldsymbol{x}$と目的変数$y$が欲しいのでしたね.df_trainから複数の説明変数$\boldsymbol{x}$が入った配列と,目的変数$y$が入った値を同じ操作で取り出す必要があります.今回の目的は,データ$i$番目に格納されている複数の説明変数train[i]に対応する目的変数train_ans[i]を取り出せるようにすることです.どのようなコードでロジスティック回帰をされるのかわかりませんが,一般的には以上のようにする必要があります.

上のデータに対してdf_train[n]を行うと説明変数nのデータ列が抽出されたpandas.Series1が得られますね.値の例としてはdf_train[0]とすると,説明変数0のデータ0からn-1までが並んだ[1, 2, 3, ..., 100, 101]になります.

コード1行目
train = np.asarray([df_train[d].tolist() for d in range(1,8)])

ここの処理は,そのようにして得られた説明変数をリスト内包表記を用いて説明変数1から7までを抽出しています.その上でpandas.Series.tolist()を行うことで,後ほどnp.asarrayを用いたnumpy.ndarrayへの変換を行えるようにしています.コメントには8までとありますが誤りであることを指摘します.正しくは8未満まで,すなわち7までです.

コード1行目で得られたnumpy配列trainは以下のようになります.

コード1行目時点で得られた配列
train = [
    [データ0, データ1, ..., データn-2, データn-1], # 説明変数1のデータ
    [データ0, データ1, ..., データn-2, データn-1], # 説明変数2のデータ
    [データ0, データ1, ..., データn-2, データn-1], # 説明変数3のデータ
    [データ0, データ1, ..., データn-2, データn-1], # 説明変数4のデータ
    [データ0, データ1, ..., データn-2, データn-1], # 説明変数5のデータ
    [データ0, データ1, ..., データn-2, データn-1], # 説明変数6のデータ
    [データ0, データ1, ..., データn-2, データn-1]  # 説明変数7のデータ
]

表で書くと

train データ0 データ1 データ2 ... データn-2 データn-1
説明変数1 101 100 99 ... 1 0
説明変数2 2 4 6 ... 200 202
... ... ... ... ... ... ...
説明変数7 3 6 9 ... 300 303

でしょうか.これに対してtrain.Tすなわち転置を行うと,

コード2行目で得られた配列
train = [
    [説明変数1, 説明変数2, ... , 説明変数7], # データ0番目
    [説明変数1, 説明変数2, ... , 説明変数7], # データ1番目
    [説明変数1, 説明変数2, ... , 説明変数7], # データ2番目
    ...
    [説明変数1, 説明変数2, ... , 説明変数7], # データn-2番目
    [説明変数1, 説明変数2, ... , 説明変数7]  # データn-1番目
]

になります.奇しくも最初の形式と同じ形になり,表で書くと

train.T 説明変数1 説明変数2 ... 説明変数7
データ0 101 2 ... 3
データ1 100 4 ... 6
データ2 99 6 ... 9
... ... ... ... ...
データn-2 1 200 ... 300
データn-1 0 202 ... 303

になりますね.転置train.Tしてできた配列trainの0番目にアクセスtrain[0]をすると,複数の説明変数が並んだデータ0すなわち[101, 2, ..., 3]を取り出すことができます.

train_ansに関しては,目的変数をそのまま取り出せているので特に転置の必要はありません.trainと同様,pandas.Seriesnumpy.ndarrayに変換しているだけですね.
 これでデータの任意の要素と,それを用いて表現したい目的変数の任意の要素を同様の記述で取り出して書くことができるようになりました.train[i]に対応するのはtrain_ans[i]といった具合にです.

以上でロジスティック回帰における複数の説明変数$\boldsymbol{x}$と目的変数$y$を同じ要素へのランダムアクセス[]を用いて配列から取り出して扱うことができるようになりましたね.先に書いたロジスティック回帰の式と合わせてかくと

train_pred[i] = 1 / (1 + np.exp(alpha + np.dot(train[i], beta)))

のようにして$i$番目の教師データを用いて得られた予測結果train_pred[i]と正しい値train_ans[i]を用いて予測精度を評価することができます.

結論

  • .Tを使用している意味
    • 転置をするのは,データを各個取り出して説明変数たちと目的変数の扱いを対応づけるためです.
    • データの$i$番目を取り出して,$i$番目に存在する複数の説明変数train[i]から$i$番目の目的変数train_ans[i]を表現する.という対応関係を作りたいからです.
    • そもそもpandas.DataFrameに対してアクセス[]したときの状態がおかしいまであります.本来あるべき姿に修正したまでのことです.
  • .tolist,np.asarrayを使用している意味
    • ロジスティック回帰では行列計算を含みますので,データを保持しているPandasから数値計算に特化したライブラリNumpyの配列numpy.ndarrayで扱いたく,変換したまでのことです.やろうと思えば最初のdf_trainたるpandas.DataFrameのままでもできます.

私なら上のようには書かないで,次のように書きます.

train = np.asarray(df_train[1:8].values).T

リスト内包表記を用いたデータの取り出しもする必要はなく,また転置もすぐに使えますのでこちらの方がわかりやすいと思います.

testに関しても同様です.

終わりに

 配列操作とライブラリの扱いに関して理解が浅い印象を受けました.「自分で調べたこと」の内容に書かれている以上のことは説明できないぐらいに,調べられたことで十分な説明が可能ですが,自身でこれを理解されるまでに至っていないように伺えます.
 初学者はまずロジスティック回帰等を実装する応用分野ではなく,基礎的な範囲から学習されることをお勧めいたします.基礎から勉強している時間がなく応用分野から学習するにしても,自分で配列の中身をprint()して追っかけていくなどの方法をとって,データがどのように加工されているかを見るということをしていただけると理解が深まるはずです.私はその方法で上のように説明させていただきました.
 プログラミングの世界は「 アルゴリズム2データ構造 」の2本の柱で支えられていると言っても過言ではありません(過言かも).プログラムを書くためにアルファベットや記号を並べるだけではないことは確かです.今学習されているロジスティック回帰を「アルゴリズム」に当てはめるにしても,それを実装するための配列等の「データ構造」を理解していないのであれば知識として重大な欠損になっていると思います.ここで出てきた「転置」や「リスト内包表記」も配列操作を行う「アルゴリズム」です.自分がプログラミングして作成したデータ構造に対して適用したアルゴリズムの動作が理解されていないようでは非常にまずいかと思われます.

 また,次回からは参考にされている書籍やブログ等も併記するようにお願いいたします.プログラムの前後がわからないのでどういうデータセットだったかやどういうコードでどのような意図があるのかわからずじまいでした.

  1. df_trainすなわちpandas.DataFrameに対してランダムアクセス[]を行うと,得られるのはpandas.Seriesになります.

  2. https://qiita.com/square1001/items/6d414167ca95c97bd8b2#0-%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0%E3%81%A8%E3%81%AF

1Like

Comments

  1. @yuuuuu03

    Questioner

    ご丁寧にご解説ありがとうございます。来月から始まる大学の授業で、春休み中にコードを書いて理解してみようというものでした。まだまだ全然勉強(努力)が足りていない中、詳細な解説と指摘をいただき感謝しています。Webや図書館で調べても理解しきれず、ここで質問させていただきました。次回からは調べた出典元も記載するように気をつけます。
  2. 非常に良い心がけだと思います。データの処理過程を可視化する力を身に着けていってくだされば上のような心配はなくなるかと思います。応援してます。本質問をクローズにしていただいて終了になります。頑張ってください。

Your answer might help someone💌