fantom_zona’s diary

Impact the world!!!

骨リモデリング・骨代謝における力の役割について

骨は、周囲の力学的刺激に誘導されるリモデリングによって、構造を変化させ適応的に身体を支える構造を維持している。例えば、宇宙飛行士や寝たきりの患者では、骨への力学的負荷が掛からないため、骨量の減少が認められる。このように、力学的刺激は骨構造の維持に重要な因子であることが分かっている。

骨リモデリングは、破骨細胞による骨吸収と骨芽細胞による骨形成のバランスによって制御されている。骨系細胞の一つである骨細胞は、骨への力学的負荷に応答しリモデリングを担う細胞群の活動を調節するメカノセンサーとしての役割を果たすと考えられている。骨細胞が力学的負荷を感知すると、NOやプロスタグランジンを産生することで、骨形成の方向に促進させることが知られている。骨細胞の力学刺激の形態としては、間質液流や静水圧上昇などが提案されているものの、未だそのメカニズムに関しては不明な点が多い。

 

**参考文献

CiNii Dissertations - 骨細胞の力学刺激感知およびカルシウム応答伝播のバイオメカニクス

Bone remodelling at a glance. - PubMed - NCBI

Tinderで遊ぶ

こんにちは。
色々辛いことがあって、むしゃくしゃしたのでTinderで色々遊んでみました。
とりあえず今回はTinderAPIを使って見ました。

Pynderの取得

pynderというものを使用しました。

git clone https://github.com/charliewolf/pynder
cd pynder 
python setup.py install

実は、pipでも入れることが可能なのですが、こちらのライブラリでは色々エラーが出てきてしまいます。どうやらgit cloneの方ではそれらのエラーは出てこないようなので、こちらで入れるようにしましょう。

トークンの取得

次に、facebookトークンを取得します。
https://tinderface.herokuapp.com/に取得方法が記載されているとの情報が多かったのですが、リンク先がメンテナンス中でした。
8月時点でもメンテナンス中との情報があったので、諦めて別の方法を模索しました。
まず、web版のTinderにログインし、左クリックで検証を選択。Networkのタブを選択し、Nameのやつを色々選択していくとX-Auth-Tokenというのがあります。これがトークンですが、2時間で変更されてしまうので注意しましょう。

やっとTinderで遊べる

import pynder
token="先ほどのトークン"
session=pynder.Session(XAuth_token=token)

helpでみてみるとこんな感じです。

"""
class Session(builtins.object)
 |  Methods defined here:
 |  
 |  __init__(self, facebook_token=None, XAuthToken=None, proxies=None, facebook_id=None)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |  
 |  get_fb_friends(self)
 |      Returns array of all friends using Tinder Social.
 |      :return: Array of friends.
 |      :rtype: Friend[]
 |  
 |  matches(self, since=None)
 |  
 |  nearby_users(self, limit=10)
 |  
 |  profile = <cached_property.cached_property object>
 |  update_location(self, latitude, longitude)
 |  
 |  update_profile(self, profile)
 |  
 |  updates(self, since=None)
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)
 |  
 |  banned
 |  
 |  can_like_in
 |      Return the number of seconds before being allowed to issue likes
 |  
 |  likes_remaining
 |  
 |  super_likes_remaining
"""

get_fb_friendsというmethodは、facebook上の友達でtinderをしているユーザーを出すmethodのようですが何も出てきませんでした。(facebookではTinderを使用していることが秘匿される様配慮されているはずだが......。)1年ほど前まで使えていたようですが、APIの仕様が変更したらしく現在は使用できません(https://github.com/fbessez/Tinder)。少し残念な気持ちになりました。
nearby_usersは近隣のユーザーを出してくれます。defaultの数が10になっていますが、なぜか無限に出てきてしまいます。カウンタを付けて適度に出しましょう。そうしないと、429 Too Many Requestsとなってしまいます。リクエスト拒否のペナルティが12時間ほどあるので気をつけましょう。表示条件は、Session.profileのmethodから色々みることが可能です。

me=Session.profile
print(me.distance_filter) #指定距離
print(me.age_filter_min) #表示するユーザーの年齢下限
print(me.age_filter_max) #表示するユーザーの年齢上限

他にもsession.update_locationで自分の位置を自由に変更することが可能です。使い方としては特定の大学の位置を指定するとかですかね。丸の内OLや港区女子を狙ったりすることも出来るかもしれません。
短いですが、とりあえずPynderの使い方としてはこれくらいで。もし続編があるとしたら画像処理とテキスト解析でしょうかね。

アンブロキソールの効果

今回はこの論文

アンブロキソールと言えば痰切りとして誰もが知るところだが、具体的な機序についてはそういえばよく知らない。医学生程度ではこういう機序の分からないものは結構あり、実際教科書に載っているような生理学や分子生物学に基づかない機序であることが多いようだ。こういうのは幾らでもあり、アンブロキソールを始め、カルボシステインだとかマグミットだとかミヤBMだとかかなり頻繁に使用される薬剤にも存在すると思われる。だから、アンブロキソールなんてものがautophagy-lysosome系に作用するなんてのは僕にとってかなり驚きである。

Gaucher病の原因変異として知られていたGBA1変異が、GWASの結果から、実はParkinson病の相当なリスクファクターであることは界隈では有名だが、まさかアンブロキソールがGCaseのシャペロンだなんてことは全く知らなかった。論文の内容としては、アンブロキソールがautophagyを抑制しexocytosisを促進したり、ミトコンドリアの生合成を促進するという内容。α-synuclein代謝まで言及されており、リン酸化α-synucleinが減少することからParkinson病を抑制する方向に行くのではないかと示唆されている。(僕はそれは怪しいと思うが)

discussionで機序の仮説として書かれているのは、アンブロキソールがlysosomeに移行しexocytosisやCa2+放出を促進し、autophagyの抑制やTFEBの活性化が起きるということだそうだ。(仮説を示すデータはこの論文では示されていない。)ちなみに、実際にアンブロキソールを内服したら神経細胞に影響を与えるか?という考察もされており、内服だけではこの実験の濃度には達しないだろうと書かれている。

GBAやアンブロキソールの先行研究を知らないのでなんとも言えないとこだが、非常に面白い内容だと思った。アンブロキソールがいつ発売されたのか知らないが、おそらく新しい薬ではないだろうと思う。こういう昔ながらの薬の機序は意外と面白いような気がしていて、マクロライドの抗炎症作用だとかメトホルミンのGLP-1分泌促進作用だとか意外な関係が調べてて面白い。余談だが、この前薬屋さんの説明で聞いた話では、メトホルミンにGLP-1促進作用があるので、DPP4との組み合わせが非常に良いらしい。あと、ビグアナイドを内服してる際は休薬しないと造影剤を基本的には使ってはいけないが、実のところ、メトホルミンではそんなに乳酸アシドーシスが起きないのではないかとかなんとか。データがきちんとあるのかは知らないが。でも乳酸アシドーシス怖いもんね。

THのIPFに対する効果の論文

これ。

 

肺の遺伝子発現データベースからIPFにDIO2が高発現していることを発見し、患者検体も用いて検証。DIO2はT4→T3への変換を担うことから、THがIPFになんらかの修飾を加えているだろうということでブレオマイシンやTGF-β1の肺線維症モデルマウスに対してTHを試したところ、実はTHが線維化を抑制していたという。しかも、全身投与じゃなくて吸入だけでもニンテタニブやピルフェニドンに劣らない効果を示してる。電顕で見てみたところ、ミトコンドリアが変性しアポトーシスしていたとこのことで、PINK1やPCG-1αの関与を疑い、それを立証している。

ストーリーとしてはTHは変性したミトコンドリアのクリアランスと正常ミトコンドリアの増殖を促すというところ。

 

驚きの結果。組織像が綺麗だ。血ガス所見がどうなのかが気になるところ。マウスの血ガスをしてないなんてことはないだろうから、血ガスは良いデータが得られなかったのかな?こういう意外な関係の発見、大好きなのでもっともっと探索して行きたい。

deepchem動かしてみました①

deepchemというライブラリを勉強してみようと思います。チュートリアルからまずやっていこうと思います。日本語の記事が少ないので、自分用のメモとして記録に残そうと思います。deepchemの溶解度予測のチュートリアルを一個一個読んでいこうと思います。

データの読み込み

%load_ext autoreload
%autoreload 2
%pdb off

しょっぱなのautoreloadがまず不明でしたが、このページに解説がありました。チュートリアルの方では%load_ext autoreloadはありませんでしたが、これを加えるとipython notebookではエラーが出ないようです。
%pdb offの方は不明です。pdbはデバッガらしいですが......誰かわかったら教えてください。

from deepchem.utils.save import load_from_disk

dataset_file= "./deepchem/datasets/delaney-processed.csv"
dataset = load_from_disk(dataset_file)
#ソースコードを見るとload_from_diskはcsvじゃなくても読み込める関数
dataset.head()

f:id:fantom_zona:20180322070209p:plain
読み込んだcsvの形式はこういう形のようです。今回行いたいのは、smiles形式から溶解度の予測をすることなので、それ以外のものは全て使いません。

描画

import tempfile
from rdkit import Chem
from rdkit.Chem import Draw
from itertools import islice
from IPython.display import Image, HTML, display

def display_images(filenames):
    """Helper to pretty-print images."""
    imagesList=''.join(
        ["<img style='width: 140px; margin: 0px; float: left; border: 1px solid black;' src='%s' />"
         % str(s) for s in sorted(filenames)])
    display(HTML(imagesList))
#空白にpngをjoinで足していってHTML表示する
    
def mols_to_pngs(mols, basename="test"):
    """Helper to write RDKit mols to png files."""
    filenames = []
    for i, mol in enumerate(mols):
        filename = "%s%d.png" % (basename, i)
        Draw.MolToFile(mol, filename)
        filenames.append(filename)
    return filenames
#Draw.MolToFile: mol object->pngにする
#ファイル名のリストにする

num_to_display = 8
molecules = []
for _, data in islice(dataset.iterrows(), num_to_display):
    molecules.append(Chem.MolFromSmiles(data["smiles"]))
display_images(mols_to_pngs(molecules))
#MolFromSmiles: smiles->mol object
#display_imagesはファイル名をしているすると表示してくれる関数

RDKitではmol objectというのを生成して、そこから描画ができるようです。
上の流れでは、
Chem.MolFromSmiles: smiles→mol object
Draw.MolToFile : mol object→png
として画像を出力し、display_images(filename)で画像を表示しています。
f:id:fantom_zona:20180324080224p:plain

特徴量の取得

このチュートリアルでは、ECFP4という手法で化合物の特徴量を取得しています。
化合物の局所的な構造を元にした特徴量をfingerprintというらしく、ECFPは主だった特徴量として有名なようです。

import deepchem as dc

featurizer = dc.feat.CircularFingerprint(size=1024)

loader = dc.data.CSVLoader(
      tasks=["measured log solubility in mols per litre"], smiles_field="smiles",
      featurizer=featurizer)
dataset = loader.featurize(dataset_file)

dc.data.CSVLoader()は、tasksとsmikes_filedを指定するとcsv形式データを取り込むclassですが、methodとしてfeaturizeが用意されています。featurizeは、事前に定義したfeaturizerを利用してsmiles形式から勝手に特徴量を取得してくれるため便利です。
CSVloaderで出力されるオブジェクトは、deepchemではDatasetオブジェクトとして扱われています。Datasetオブジェクトは、(X, y, w, ids) という値を持っています。Xは特徴量、yは出力、wは重み、idsはID(今回の場合はsmiles形式を返す)です。deepchemのDatasetに関するチュートリアルもあるので今後それも記事にできたらと思います。

前処理

データをtrain/splitに分け、前処理は正規化を行います。

splitter = dc.splits.ScaffoldSplitter(dataset_file)
train_dataset, valid_dataset, test_dataset = splitter.train_valid_test_split(dataset)

transformers = [
    dc.trans.NormalizationTransformer(transform_y=True, dataset=train_dataset)]

dc.splits.ScaffoldSplitterでは、分子構造の偏り?が生まれないようにうまくsplitしてくれているらしいです。他にもrandom splitなども選択できるようです。

学習部分

deepchemはsklearnをラップしています。sklearnの異なるモデルに対して、SklearnModelに突っ込めば、fit(dataset object)で学習してくれます。学習したモデルはsave()で保存してくれます。save()では、sklearnのjoblib.dumpが背後で動いてくれます。

from sklearn.ensemble import RandomForestRegressor

sklearn_model = RandomForestRegressor(n_estimators=100)
model = dc.models.SklearnModel(sklearn_model)
model.fit(train_dataset)

評価部分

from deepchem.utils.evaluate import Evaluator

metric = dc.metrics.Metric(dc.metrics.r2_score)
#r2_scoreはdc.metrics内でimportしてあるので呼び出せる

evaluator = Evaluator(model, valid_dataset, transformers)
#class Evaluator(model, dataset, transformers, verbose=False)

r2score = evaluator.compute_model_performance([metric])
print(r2score)

Evaluator.compute_model_performance()で、metricの計算をしてくれます。
適当に決めた木の数(=100)では、決定係数は0.2くらいでした。

チューニング

木の数と特徴量の数を適当に振り、最適なものを選び出します。deepchem.hyper.grid_search moduleにあるHyperparamOptでグリッドサーチしてくれます。

def rf_model_builder(model_params, model_dir):
    sklearn_model = RandomForestRegressor(**model_params)
    return dc.models.SklearnModel(sklearn_model, model_dir)
params_dict = {
    "n_estimators": [10, 20,40, 80, 160, 320],
    "max_features": ["auto", "sqrt", "log2", None],
}

metric = dc.metrics.Metric(dc.metrics.r2_score)
optimizer = dc.hyper.HyperparamOpt(rf_model_builder)
#ただのclass
#methodでhyperparam_searchが使用できる
#dictionaryでパラメータを指定すれば検索してくれる
best_rf, best_rf_hyperparams, all_rf_results = optimizer.hyperparam_search(
    params_dict, train_dataset, valid_dataset, transformers,
    metric=metric)

木は10個、特徴量の数はルートが最適でした。R2の値は0.3くらいでした。
結果を出力してみるとそこそこ学習しているかなといったところでしょうか。ただし、大小関係のみが保たれている傾向があり、実際の値の予測はだめそうです。
deepchemのチュートリアルではランダムフォレストに加えて、ニューラルネットも使っていましたが、deepじゃなさそうなのにdeepって書いてあり、しかも使っている関数がdeepchem 2.0.0では削除されているようなので割愛しました。

task = "measured log solubility in mols per litre"
predicted_test = best_rf.predict(test_dataset)
true_test = test_dataset.y
plt.scatter(predicted_test, true_test)
plt.xlabel('Predicted log-solubility in mols/liter')
plt.ylabel('True log-solubility in mols/liter')
plt.title(r'RF- predicted vs. true log-solubilities')
plt.show()

f:id:fantom_zona:20180324080257p:plain

感想

以上、csvから予測までのチュートリアルを動かしてみたという話でした。
少しだけdeepchemが使えるようになった気がしてきました。今回はグラフ構造の学習をしなかったので、グラフ構造の学習とかもやっていきたいと思いました。

KaggleのTitanicにトライしてみた話

右も左も分からない人間が、kaggleに挑戦してみた話です。

とりあえず登録してこのページに行ってtrain.csv, test.csvをダウンロード。
train.csvで予測モデルを構築して、test.csvを予想して提出という形。
とりあえず色々データを覗いてみる。

import pandas as pd 
import matplotlib.pyplot as plt
%matplotlib inline

train=pd.read_csv("./Titanic/train.csv")
train.head(20)

こんな感じに先頭の20個を表示してみると、Cabinに欠損値が多いことが分かる。Name、Sex、Ticket、Cabin、Embarkedは数値じゃないので、捨てるか数値に変換したい。
f:id:fantom_zona:20180321042244p:plain

ヒストグラムにしてもう少し覗いてみる前に、SexとEmbarkedを数値に変換して、Name、Ticket、Cabinのデータは捨てることにする。ヒストグラムにする際には、欠損値はdropna()で落としておく。

train.replace({"male": 0, "female": 1}, inplace=True);
train.replace({"Q": 0, "S": 1, "C": 2}, inplace=True);
train.drop(["Name", "Ticket", "Cabin"], axis=1, inplace=True);

dead_data=train[train.Survived==0]
survived_data=train[train.Survived==1]

plt.hist([dead_data.Age.dropna(), survived_data.Age.dropna()], label=["dead"," survived"])
plt.legend()
plt.titile("age")
plt.show()

plt.hist([dead_data.Fare.dropna(), survived_data.Fare.dropna()], label=["dead"," survived"])
plt.legend()
plt.titile("fare")
plt.show()


plt.hist([dead_data.Embarked.dropna(), survived_data.Embarked.dropna()], label=["dead","survived"])
plt.legend()
plt.titile("embarked")
plt.show()

f:id:fantom_zona:20180321043953p:plainf:id:fantom_zona:20180321044002p:plainf:id:fantom_zona:20180321044011p:plain

欠損値の補完は適当に中央値にしておく。

train.Age.fillna(test.Age.median(), inplace=True);
train.Fare.fillna(test.Fare.median(), inplace=True);
train.Embarked.fillna(1, inplace=True);

今回行う学習は2値分類なので、有名どころのSVMとRandomforestの比較をしてみる。
データをtrain/testに分けたいときは、sklearnのtran_test_splitが便利。
分けたあとは、ラベルと入力を分けて学習させて評価すれば終わり。

import numpy as np
from sklearn.cross_validation import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn import svm
from sklearn.metrics import classification_report, accuracy_score

train1, test1=train_test_split(train, test_size=0.2, random_state=None)
train_feature=train1.drop("Survived", axis=1)
train_label=train1.Survived
test_feature=test1.drop("Survived", axis=1)
test_label=test1.Survived

clf = svm.SVC()
clf.fit(train_feature, train_label)
svm_pred=clf.predict(test_feature)
print(classification_report(test_label, svm_pred))
print(accuracy_score(test_label, svm_pred))

rf=RandomForestClassifier()
rf.fit(train_feature, train_label)
rf_pred=rf.predict(test_feature)
print(classification_report(test_label, rf_pred))
print(accuracy_score(test_label, rf_pred))

こちらの手元では、SVMが0.6、RFが0.8弱だったので、RFを採用して提出することにした。

#testデータの前処理
test.replace({"male": 0, "female": 1}, inplace=True);
test.replace({"Q": 0, "S": 1, "C": 2}, inplace=True);
test.drop(["Name", "Ticket", "Cabin"], axis=1, inplace=True);
test.Age.fillna(test.Age.median(), inplace=True);
test.Fare.fillna(test.Fare.median(), inplace=True);
test.Embarked.fillna(1, inplace=True);

rf=RandomForestClassifier()
rf.fit(train.drop("Survived", axis=1), train.Survived)
rf_pred_all=rf.predict(test)

submission = pd.DataFrame()
submission['PassengerId'] = test.PassengerId
submission['Survived'] = rf_pred_all
submission.to_csv('submission.csv', index=False)

が、、、ここまで来てKaggleのページからどうやってsubmitすればいいのか分からなくなった。。。
調べたところ、最近になってKaggleはAPIで提出する形になったようなので仕方なくpip install kaggle。kaggleのページに行って、"My account">"create new api token"に進むと、kaggle.jsonのダウンロードが開始される。homeディレクトリに.kaggleを作成してkaggle.jsonをそこに入れたのち、

!kaggle competitions submit -c titanic -f submission.csv -m "RF all data"

で提出完了!データを落としてくるところもAPIでいけるらしい。ほー。

感想とか。
Embarkを0,1,2にしちゃダメそうな気がしたけど、まあとりあえずやっちゃった感ある。
SVMの精度がめちゃめちゃ悪かったのがなんでなのかよく分からない。ここでは結構精度が出てるので、どこかが変なのかもしれない。
前処理はもっと工夫するべきなんだろうけど、初めてのKaggleということで大目に見てもらいたい。