こんにちは!ぼりたそです!以前、PubChemから2000件の化合物データを取得しましたが、今回はこの化合物データのクレンジング(前処理)を行ったので、ご紹介します。
この記事は以下のポイントでまとめています。
このデータの最終的な使い道は化合物のMordred記述子を説明変数、XlogPを目的変数としてベイズ最適化を実行することです。そのために以下に記載したデータのクレンジングを行いました。
- 目的変数の欠損値処理
- 化合物のMordred記述子計算
- 不要な説明変数の削除
以下に今回使用するデータを記載しておきます。
※CSVではダウンロードできないようなので、エクセルファイル(.xlsx)をダウンロードしてCSVとしてエクスポートしていただけますと使用できるかと思います。
それでは詳細に説明していきます。
目的変数の欠損値処理
まず、目的変数の欠損値処理を行いました。
PubChemから取得した化合物データの中でも、XlogPの値を持たない欠損値があるものについて処理を行いました。
具体的には単純にXlogPを持たない化合物を削除するといったものです。以下がそのコードになります。
まず、PubChemから取得したデータをdataとして読み込みcid, XlogP, canonicalsmilesのみを抽出しています。cidは後にインデックスにするために抽出しております。
import pandas as pd
# CSVファイルを読み込む
data = pd.read_csv('combined.csv')
data = data[['cid','xlogp', 'canonicalsmiles']]
# 欠損値がある行を削除する
cleaned_data = data.dropna()
# 結果を保存
cleaned_data.to_csv('combined.csv')
Mordred記述子の計算
次にMordred記述子の計算ですが、以前PubChemから取得したデータから計算しています。
手順としては、まずPubChemから取得したSMILESをRDkitの分子オブジェクトに変換します。
その後、RDkitの分子オブジェクトを使用してCalculatorにてMordred記述子を計算しています。
PubChemから得られるSMILESから直接Mordred記述子を計算しようとするとエラーを吐かれてしまうので注意です。どうやらPubChemから取得できるcanonicalsmilesはMordredの計算には対応していないようですね。
今回の目的変数はXlogP、説明変数はMordred記述子なのでそれぞれ分割してCSVにて保存しています。インデックスはPubChemのcidをそのまま割り当てています。(目的変数と説明変数の照合に便利であるため)
import pandas as pd
from mordred import Calculator, descriptors
from rdkit import Chem
# データフレームの読み込み
data = pd.read_csv('combined.csv')
data = data[['cid','xlogp', 'canonicalsmiles']]
data = data.set_index('cid')
# SMILESをRDKitの分子オブジェクトに変換する関数
def smiles_to_mol(smiles):
mol = Chem.MolFromSmiles(smiles)
return mol
# データフレームのSMILESカラムに対して関数を適用し、新たなデータフレームを作成する
data['Molecule'] = data['canonicalsmiles'].apply(smiles_to_mol)
# SMILESから記述子を計算するためのCalculatorオブジェクトを作成する
calc = Calculator(descriptors)
# SMILESカラムの記述子を計算する
descriptors_data = calc.pandas(data['Molecule'])
# 計算結果をデータフレームに追加&Mordredを説明変数としてデータフレーム化
data_x = pd.concat([data, descriptors_data], axis=1)
data_x = data_x.drop(columns=['xlogp', 'canonicalsmiles', 'Molecule'])
#XlogPを目的変数としてデータフレーム化
data_y = data['xlogp']
# CSVとして保存
data_x.to_csv('data_x.csv')
data_y.to_csv('data_y.csv')
ちなみに説明変数であるMordred記述子のデータフレームのサイズを見てみましょう。
import pandas as pd
# データフレームを作成
data_x = pd.read_csv('data_x.csv')
# データフレームのサイズを出力
rows, cols = data_x.shape
print("行数:", rows)
print("列数:", cols)
#出力結果
#行数: 1989
#列数: 1826
1826個も記述子があるんですね…とんでもない数ですね…
不要な説明変数の削除
最後に計算したMordred記述子の中で不要な変数の削除を実行しました。
今回、不要な説明変数としたのは以下の条件に当てはまる記述子です。
- 文字列を含む
- 分散が0
- 欠損値を含む
実際に実行したコードを以下に示します。
ちなみに文字列が含まれるカラムを先に削除しておかないと分散が0のカラムを計算するときにエラーが出てしまうので注意しましょう。
import pandas as pd
import numpy as np
# データフレームを作成
data_x = pd.read_csv('data_x.csv')
# 文字列が含まれるカラムを削除
data_x = data_x.select_dtypes(exclude=['object'])
# カラムの値の分散が0のカラムを削除
data_x = data_x.loc[:, data_x.var() != 0]
# 欠損値があるカラムを削除
data_x = data_x.dropna(axis=1)
#インデックスをCIDとする
data_x = data_x.set_index('cid')
# CSVとして保存
data_x.to_csv('data_x.csv')
削除後のデータ数を見てみましょう。
import pandas as pd
# データフレームを作成
data_x = pd.read_csv('data_x.csv')
# データフレームのサイズを出力
rows, cols = data_x.shape
print("行数:", rows)
print("列数:", cols)
#出力結果
#行数: 1989
#列数: 917
列数を見ると元々1826列あったものが半分程度である917個になっていますね。かなりの記述子が削減されたことがわかります。
終わりに
以上でデータ前処理についての説明になります。実はデータ分析を実行するよりもデータの前処理の方が手間がかかるんですよね…
次はいよいよベイズ最適化を実行してみたいと思いますのでお楽しみに!