はじめに
この記事は医薬品業界の中でも後発医薬品 (ジェネリック)に関する記事です。
データ分析のアプローチとして、こんな方法もあるんだな程度で見ていただければと思います。
目次
- 背景
- 目的
- 方法
- 結果・考察
- 所感
- 今後の予定
背景
私はジェネリック医薬品企業に勤める者です。恥ずかしながら、競合他社がどこなのか把握していませんでした。
一方、PMDA (医薬品機構)はMF公示資料を月に2度更新しています (リンク)。MF (Master File)とは、医薬品の原薬 (有効成分)を製造する企業がPMDAに登録する製造方法のノウハウのことです。このMFを医薬品の製剤を製造する企業が参照することで、原薬製造企業はノウハウを開示することなく、医薬品が作ることができます。
つまり、このMF公示資料を分析すれば、競合他社を把握することができるのでは?と考えました。
目的
以下2つのタスクを考えました。
競合他社の動向調査:他社がどのくらいアクティブかを分析する
競合他社の技術力調査:他社がどのくらい合成難度の高い化合物に挑戦しているかを分析する
方法
競合他社の動向調査
- これまで収集したMF公示資料 (excel)をpandasで一つのDataFrameにする。
- 横軸に年代、縦軸に会社名、バブルの大きさをMF件数とするバブルチャートを作成する。
競合他社の技術力調査
- 上記で1つにしたDataFrameを使用する。
- 化合物名+casで検索してcas番号の列を挿入する。
- cas番号からpubchemでsmilesを取得後、rdkitでMolObjectを入手する。
- MolObjectからSynthetic Accessibility score (sa)を算出し、平均及び標準偏差を取得する。
実行環境は以下の通りです。
- python 3.7.11
- pandas 1.3.4
- numpy 1.20.3
- rdkit 2020.09.1
- matplotlib 3.4.2
- requests 2.27.1
- re 2.2.1
- pubchempy 1.0.4
結果・考察
競合他社の動向調査
まずはライブラリのインポートです。フォント指定をしないと、日本語が文字化けするため、plt.rcParams['font.family'] = 'MS Gothic'
でフォント指定しています。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from glob import glob
plt.rcParams['font.family'] = 'MS Gothic'
これまで収集したMF公示資料をMF_data
というフォルダに保存し、それを結合しました。メモリ使用量を削減するために、結合するごとに重複を除いていきました。
for i, file in enumerate(glob('MF_data/*.xlsx')):
df = pd.read_excel(file)
if i==0:
df_con = df
continue
df_con = pd.concat([df_con, df])
df_con = df_con.drop_duplicates()
df_con.head()
"""
MF登録番号 初回登録年月日 最新登録年月日 登録区分 登録品目名 登録者氏名 登録者住所 原薬等国内管理人の名称 原薬等国内管理人の住所
0 231MF10089 2019/04/11 2019/04/11 医薬品等原薬 セフメタゾール Amicogen(China)Biopharm Co.,Ltd. 山東省済寧市詩仙路1688号 NaN NaN
1 231MF10088 2019/04/11 2019/04/11 医薬品等原薬 アンブロキソール塩酸塩 ALCHYMARS ICM SM PRIVATE LIMITED. “ADWAVE TOWERS” C/4,THIRD FLOOR NEW NO.17(OLD ... NaN NaN
2 231MF10087 2019/04/11 2019/04/11 医薬品等原薬 ソリスロマイシン 富士フイルムワコーケミカル株式会社 宮崎県宮崎市清武町木原3558番地 NaN NaN
3 231MF10086 2019/04/11 2019/04/11 医薬品等原薬 ブリンゾラミド Icrom S.p.A. Via Delle Arti 33,20863 Concorezzo (MB), Italy NaN NaN
4 227MF10236 2015/09/24 2019/04/11 医薬品等原薬 ゾルピデム酒石酸塩 Glenmark Life Sciences Limited Glenmark House,HDO Corporate Building,Wing A,B... NaN NaN
"""
結合したDataFrameの詳細を確認します。
print(df_con.shape)
print(df_con['登録区分'].unique())
df_con.info()
"""
(6552, 9)
['医薬品等原薬' '添加剤' '医療機器原材料' 'その他']
<class 'pandas.core.frame.DataFrame'>
Int64Index: 6552 entries, 0 to 4306
Data columns (total 9 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 MF登録番号 6552 non-null object
1 初回登録年月日 6552 non-null object
2 最新登録年月日 6552 non-null object
3 登録区分 6552 non-null object
4 登録品目名 6552 non-null object
5 登録者氏名 6552 non-null object
6 登録者住所 6552 non-null object
7 原薬等国内管理人の名称 33 non-null object
8 原薬等国内管理人の住所 33 non-null object
dtypes: object(9)
"""
ここまでで分かったことを列挙します。
- 6552行、9列のデータである。
- MF登録番号は通し番号である。
- 初回登録年月日と最新登録年月日はよくわからなかったが、最新登録年月日が申請時の年月日である。
- 登録区分は、医薬品等原薬、添加剤、医療機器原材料、その他がある。
- 登録品目名は、原薬の化合物名が記されている。
- 登録者氏名は、登録した会社名である。
- 列名は「MF登録番号、初回登録年月日、最新登録年月日、登録区分 登録品目名、登録者氏名、登録者住所、原薬等国内管理人の名称、原薬等国内管理人の住所」
- 原薬等国内管理人の名称、原薬等国内管理人の住所は33行しか値がないので、情報としては価値がない
バブルチャートを作る準備をします。こちらを参考にして、行を会社名、列を年数、データを申請数としたDataFrameを作成しました。
初回登録年月日と最新登録年月日をdatetime
に変換し、そこから年だけを抽出してyearという列を生成しました。新たなDataFrameとして、行に登録者氏名、列にyearを持つdf_ana
を作り、順次申請数を入力していきました。最後に、yearが年代順になっていなかったのでソートして下準備が完了しました。
df_con["初回登録年月日"] = pd.to_datetime(df_con["初回登録年月日"])
df_con["最新登録年月日"] = pd.to_datetime(df_con["最新登録年月日"])
df_con['year'] = df_con["最新登録年月日"].dt.year
df_ana = pd.DataFrame(index=df_con['登録者氏名'].unique(), columns=df_con['year'].unique())
for reg in df_ana.index:
for year in df_ana.columns:
a = (df_con['year']==year)&(df_con['登録者氏名']==reg)
df_ana.loc[reg, year] = a.values.sum()
df_ana.columns = df_ana.columns.astype(int)
df_ana.reindex(columns = list(range(2005, 2023)))
df_ana.head()
2005 | 2006 | 2007 | 2008 | 2009 | 2010 | 2011 | 2012 | 2013 | 2014 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 | 2021 | 2022 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Amicogen(China)Biopharm\u3000Co.,Ltd. | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 |
ALCHYMARS\u3000ICM\u3000SM\u3000PRIVATE\u3000LIMITED. | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 |
富士フイルムワコーケミカル株式会社 | 0 | 4 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 2 | 1 | 0 | 2 | 1 | 0 | 0 |
Icrom\u3000S.p.A. | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 2 | 4 | 0 | 0 |
Glenmark\u3000Life\u3000Sciences\u3000Limited | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 3 | 0 | 4 | 3 | 1 | 1 | 7 | 2 | 6 | 2 |
いよいよバブルチャートの作成です。全ての企業をプロットするのは大変なので、Top20を表示することにしました。
まず、申請数の合計をsumという列に代入し、それをもとにソートして、Top20を抽出しました。x軸に年数、y軸に会社名を記載したバブルチャートを作成しました。plt.scatter()
のs
が初期値ではバブルの大きさが小さかったので、50倍にしています。申請数をplt.text()
でバブルの真ん中に記載しました。
df_ana['sum'] = df_ana.sum(axis=1)
df_ana = df_ana.sort_values('sum', ascending=False)
df_extract = df_ana.drop('sum', axis=1).head(20)
plt.figure(figsize=(10,10))
for n,y in enumerate(df_extract.columns):
for m,x in enumerate(df_extract.index):
num = int(df_extract[y][x])
plt.scatter(y, x, s=num*50)
if num!=0:
plt.text(y, x, str(num), ha='center', va='center', c='white')
plt.xlim(df_extract.columns.min()-1, df_extract.columns.max()+1)
plt.xticks(np.arange(df_extract.columns.min()-1,
df_extract.columns.max()+1, 1), rotation=45)
plt.xlabel('year')
plt.title('Top 20 companies applying for MF')
plt.show()
さらに、登録区分について、円グラフを作成しました。こちらは確認程度だったので、グラフは示しませんが、Life Technologies Corporation以外は医薬品等原薬でした。当会社はその他の区分となっており、医薬品原薬の製造会社ではないことが分かりました。
fig, axes = plt.subplots(5,4, figsize=(15,15))
section = df_con['登録区分'].unique()
for ax, com, i in zip(axes.ravel(), df_extract.index, range(20)):
df_life = df_con[df_con['登録者氏名']==com]
cnt_list = []
for item in section:
try:
cnt_list.append(df_life['登録区分'].value_counts()[item])
except:
cnt_list.append(0)
ax.pie(cnt_list, labels=section, startangle=90, counterclock=False, autopct="%1.1f%%")
if len(com)>=15:
com = com[:15]
ax.set_title("{}. ".format(i+1) + com)
Top20のランキングです。
top20_com = pd.DataFrame(df_extract.index)
top20_com
"""
0
0 Life Technologies Corporation
1 株式会社 三洋化学研究所
2 Sicor Societa(`) Italiana Corticosteroidi S.r.l.
3 アルプス薬品工業株式会社
4 Olon S.p.A.
5 ダイト株式会社
6 Kyongbo Pharmaceutical Co.,Ltd.
7 Mylan Laboratories Limited
8 住友化学株式会社
9 株式会社パーマケム・アジア
10 Cambrex Profarmaco Milano S.r.l.
11 Hetero Labs Limited
12 KOLON LIFE SCIENCE,INC.
13 SANOFI CHIMIE
14 Aurobindo Pharma Limited
15 HANMI FINE CHEMICAL CO.,LTD.
16 相模化成工業株式会社
17 株式会社 大阪合成有機化学研究所
18 大和薬品工業株式会社
19 八代製薬株式会社
"""
考察
今回の調査で得た考察を以下に示します。
- MF申請数の合計でランキングしたところ、1位のLife technology (現在はthermo fisher scienticの一部)のMF内容はその他となっており、医薬品原薬を製造している企業ではなかった。
- 上位の半数は海外勢であり、企業の競争レベルは世界規模に及んでいることが読み取れた。
- 国内で勢力が衰えているのはパーマケムアジアのみであり、一方で海外勢の申請数は軒並み上昇傾向であった。
- 三洋化学研究所はジェネリック医薬品原薬の製造及び受託製造の企業。
- アルプス薬品工業は医薬品原薬だけでなく、食品添加物や化粧品も手掛ける企業。
- ダイトは医薬品原薬だけでなく、製剤事業も展開しており、製販の立場を理解できるアドバンテージがあると考えられる。
競合他社の技術力調査
まずはライブラリのインポートです。
import requests
import re
import pandas as pd
import numpy as np
import pubchempy as pcp
from rdkit import Chem
import sascorer
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = 'MS Gothic'
まずは、上記で作成したDataFrameからTop20の企業のみを抽出しました。1129行まで絞り込みました。
df_test = df_con.query('登録者氏名 in {}'.format(list(top20_com.iloc[:,0])))
df_test
"""
MF登録番号 初回登録年月日 最新登録年月日 登録区分 登録品目名 登録者氏名 登録者住所 原薬等国内管理人の名称 原薬等国内管理人の住所
26 219MF10293 2007/09/10 2019/03/29 医薬品等原薬 スルバクタムナトリウム(製造専用) Kyongbo Pharmaceutical Co.,Ltd. 174,Sirok-ro,Asan-si,Chungcheongnam-do,KOREA NaN NaN
28 218MF10710 2006/08/14 2019/03/29 医薬品等原薬 日本薬局方 スルバクタムナトリウム(製造専用) Kyongbo Pharmaceutical Co.,Ltd. 174,Sirok-ro,Asan-si,Chungcheongnam-do,KOREA NaN NaN
36 218MF10971 2006/12/01 2019/03/19 医薬品等原薬 ナロキソン塩酸塩水和物 SANOFI CHIMIE 82 Avenue Raspail 94250 Gentilly France NaN NaN
42 230MF10133 2018/09/14 2019/03/12 医薬品等原薬 メチルプレドニゾロンコハク酸エステルナトリウム SANOFI CHIMIE 82 Avenue Raspail 94250 Gentilly France NaN NaN
52 220MF10128 2008/05/15 2019/03/05 医薬品等原薬 メチルプレドニゾロンコハク酸エステル SANOFI CHIMIE 82 Avenue Raspail 94250 Gentilly France NaN NaN
... ... ... ... ... ... ... ... ... ...
2633 219MF10105 2007/03/23 2012/03/30 医薬品等原薬 イトラコナゾール Mylan Laboratories Limited Plot No.564/A/22, Road No.92, Jubilee Hills, H... NaN NaN
2821 222MF10253 2010/10/28 2010/10/28 医薬品等原薬 オロパタジン塩酸塩 KOLON LIFE SCIENCE,INC. 110,MAGOKDONG-RO,GANGSEO-GU,SEOUL,KOREA NaN NaN
3478 217MF10151 2005/05/31 2008/08/18 医薬品等原薬 マレイン酸セチプチリン「サンヨー」 株式会社 三洋化学研究所 大阪府堺市美原区多治井148-1 NaN NaN
3793 219MF10159 2007/05/15 2007/05/15 医薬品等原薬 チクロピジン塩酸塩「テバ」 Sicor Societa(`) Italiana Corticosteroidi S.r.l. Piazzale Luigi Cadorna, 4 - 20123 MILANO, Italy NaN NaN
3809 218MF10270 2006/02/17 2007/04/27 医薬品等原薬 日本薬局方 メキタジン 住友化学株式会社 東京都中央区日本橋二丁目7番1号 NaN NaN
1129 rows × 9 columns
"""
効果があるかは不明ですが、文字を小文字にしてスペースを削除しました。
df_test['登録品目名'] = df_test['登録品目名'].str.lower()
df_test['登録者氏名'] = df_test['登録者氏名'].str.lower().replace(' ', " ")
df_test['登録者住所'] = df_test['登録者住所'].str.lower().replace(' ', " ")
第一の関門は、登録品目名に記載されている化合物名からcas番号を引っ張り出すステージです。rtn_cas
という関数を作成しました。登録品目名に記載されている化合物名を受け取り、「化合物名+cas」でgoogle検索を行い、ページ中にあるcas番号を正規表現によって抽出するものです。処理にかなり時間がかかりましたが、何とか実行することができました。化合物によっては、cas番号を抽出できませんでしたが、次に進みました。
def rtn_cas(name):
name_searched = name + " cas"
num = 1 # pages that you want to search
url = "https://www.google.co.jp/search?hl=ja&num={}&q={}".format(num, name_searched)
res = requests.get(url)
if res.status_code != 200:
return False
txt = res.text
ptn = r"\d+-\d\d-\d" # cas number
result = re.findall(ptn, txt)
if len(result)==0:
return False
return result[0]
df_test['cas'] = df_test['登録品目名'].map(rtn_cas)
df_test
"""
MF登録番号 初回登録年月日 最新登録年月日 登録区分 登録品目名 登録者氏名 登録者住所 原薬等国内管理人の名称 原薬等国内管理人の住所 cas
26 219MF10293 2007/09/10 2019/03/29 医薬品等原薬 スルバクタムナトリウム(製造専用) kyongbo pharmaceutical co.,ltd. 174,sirok-ro,asan-si,chungcheongnam-do,korea NaN NaN 69388-84-7
28 218MF10710 2006/08/14 2019/03/29 医薬品等原薬 日本薬局方 スルバクタムナトリウム(製造専用) kyongbo pharmaceutical co.,ltd. 174,sirok-ro,asan-si,chungcheongnam-do,korea NaN NaN False
36 218MF10971 2006/12/01 2019/03/19 医薬品等原薬 ナロキソン塩酸塩水和物 sanofi chimie 82 avenue raspail 94250 gentilly france NaN NaN 51481-60-8
42 230MF10133 2018/09/14 2019/03/12 医薬品等原薬 メチルプレドニゾロンコハク酸エステルナトリウム sanofi chimie 82 avenue raspail 94250 gentilly france NaN NaN 2375-03-3
52 220MF10128 2008/05/15 2019/03/05 医薬品等原薬 メチルプレドニゾロンコハク酸エステル sanofi chimie 82 avenue raspail 94250 gentilly france NaN NaN False
... ... ... ... ... ... ... ... ... ... ...
2633 219MF10105 2007/03/23 2012/03/30 医薬品等原薬 イトラコナゾール mylan laboratories limited plot no.564/a/22, road no.92, jubilee hills, h... NaN NaN False
2821 222MF10253 2010/10/28 2010/10/28 医薬品等原薬 オロパタジン塩酸塩 kolon life science,inc. 110,magokdong-ro,gangseo-gu,seoul,korea NaN NaN False
3478 217MF10151 2005/05/31 2008/08/18 医薬品等原薬 マレイン酸セチプチリン「サンヨー」 株式会社 三洋化学研究所 大阪府堺市美原区多治井148-1 NaN NaN False
3793 219MF10159 2007/05/15 2007/05/15 医薬品等原薬 チクロピジン塩酸塩「テバ」 sicor societa(`) italiana corticosteroidi s.r.l. piazzale luigi cadorna, 4 - 20123 milano, italy NaN NaN False
3809 218MF10270 2006/02/17 2007/04/27 医薬品等原薬 日本薬局方 メキタジン 住友化学株式会社 東京都中央区日本橋二丁目7番1号 NaN NaN False
1129 rows × 10 columns
"""
次の関門は、cas番号からsmilesを得るステージです。pubchempyを用いて、smilesを取得しました。pubchempyはPubChemから情報を取り出せるライブラリです。cas2smiles
という関数を作成し、smilesという列に代入しました。
def cas2smiles(cas):
if cas == False:
return False
result = pcp.get_properties(['CanonicalSMILES'], cas, 'name')
if len(result)==0:
return False
smiles = result[0]['CanonicalSMILES']
return smiles
df_test['smiles'] = df_test['cas'].map(cas2smiles)
df_test
"""
MF登録番号 初回登録年月日 最新登録年月日 登録区分 登録品目名 登録者氏名 登録者住所 原薬等国内管理人の名称 原薬等国内管理人の住所 cas smiles
26 219MF10293 2007/09/10 2019/03/29 医薬品等原薬 スルバクタムナトリウム(製造専用) kyongbo pharmaceutical co.,ltd. 174,sirok-ro,asan-si,chungcheongnam-do,korea NaN NaN 69388-84-7 CC1(C(N2C(S1(=O)=O)CC2=O)C(=O)[O-])C.[Na+]
28 218MF10710 2006/08/14 2019/03/29 医薬品等原薬 日本薬局方 スルバクタムナトリウム(製造専用) kyongbo pharmaceutical co.,ltd. 174,sirok-ro,asan-si,chungcheongnam-do,korea NaN NaN False False
36 218MF10971 2006/12/01 2019/03/19 医薬品等原薬 ナロキソン塩酸塩水和物 sanofi chimie 82 avenue raspail 94250 gentilly france NaN NaN 51481-60-8 C=CCN1CCC23C4C(=O)CCC2(C1CC5=C3C(=C(C=C5)O)O4)...
42 230MF10133 2018/09/14 2019/03/12 医薬品等原薬 メチルプレドニゾロンコハク酸エステルナトリウム sanofi chimie 82 avenue raspail 94250 gentilly france NaN NaN 2375-03-3 CC1CC2C3CCC(C3(CC(C2C4(C1=CC(=O)C=C4)C)O)C)(C(...
52 220MF10128 2008/05/15 2019/03/05 医薬品等原薬 メチルプレドニゾロンコハク酸エステル sanofi chimie 82 avenue raspail 94250 gentilly france NaN NaN False False
... ... ... ... ... ... ... ... ... ... ... ...
2633 219MF10105 2007/03/23 2012/03/30 医薬品等原薬 イトラコナゾール mylan laboratories limited plot no.564/a/22, road no.92, jubilee hills, h... NaN NaN False False
2821 222MF10253 2010/10/28 2010/10/28 医薬品等原薬 オロパタジン塩酸塩 kolon life science,inc. 110,magokdong-ro,gangseo-gu,seoul,korea NaN NaN False False
3478 217MF10151 2005/05/31 2008/08/18 医薬品等原薬 マレイン酸セチプチリン「サンヨー」 株式会社 三洋化学研究所 大阪府堺市美原区多治井148-1 NaN NaN False False
3793 219MF10159 2007/05/15 2007/05/15 医薬品等原薬 チクロピジン塩酸塩「テバ」 sicor societa(`) italiana corticosteroidi s.r.l. piazzale luigi cadorna, 4 - 20123 milano, italy NaN NaN False False
3809 218MF10270 2006/02/17 2007/04/27 医薬品等原薬 日本薬局方 メキタジン 住友化学株式会社 東京都中央区日本橋二丁目7番1号 NaN NaN False False
1129 rows × 11 columns
"""
ここまででどのくらい情報が欠損したのかチェックしました。1129件中408件を抽出できていました。かなり欠損していますが、仕方がないのでこのまま進めました。
(df_test['smiles']==False).value_counts()
"""
True 721
False 408
Name: smiles, dtype: int64
"""
そして、smilesからMolObjectを返す関数smiles2mol
を作成して、MolObjectを取得しました。
def smiles2mol(smiles):
if smiles==False:
return False
mol = Chem.MolFromSmiles(smiles)
return mol
df_test['ROMol'] = df_test['smiles'].apply(smiles2mol)
df_test
"""
MF登録番号 初回登録年月日 最新登録年月日 登録区分 登録品目名 登録者氏名 登録者住所 原薬等国内管理人の名称 原薬等国内管理人の住所 cas smiles ROMol
26 219MF10293 2007/09/10 2019/03/29 医薬品等原薬 スルバクタムナトリウム(製造専用) kyongbo pharmaceutical co.,ltd. 174,sirok-ro,asan-si,chungcheongnam-do,korea NaN NaN 69388-84-7 CC1(C(N2C(S1(=O)=O)CC2=O)C(=O)[O-])C.[Na+] <img data-content="rdkit/molecule" src="data:i...
28 218MF10710 2006/08/14 2019/03/29 医薬品等原薬 日本薬局方 スルバクタムナトリウム(製造専用) kyongbo pharmaceutical co.,ltd. 174,sirok-ro,asan-si,chungcheongnam-do,korea NaN NaN False False False
36 218MF10971 2006/12/01 2019/03/19 医薬品等原薬 ナロキソン塩酸塩水和物 sanofi chimie 82 avenue raspail 94250 gentilly france NaN NaN 51481-60-8 C=CCN1CCC23C4C(=O)CCC2(C1CC5=C3C(=C(C=C5)O)O4)... <img data-content="rdkit/molecule" src="data:i...
42 230MF10133 2018/09/14 2019/03/12 医薬品等原薬 メチルプレドニゾロンコハク酸エステルナトリウム sanofi chimie 82 avenue raspail 94250 gentilly france NaN NaN 2375-03-3 CC1CC2C3CCC(C3(CC(C2C4(C1=CC(=O)C=C4)C)O)C)(C(... <img data-content="rdkit/molecule" src="data:i...
52 220MF10128 2008/05/15 2019/03/05 医薬品等原薬 メチルプレドニゾロンコハク酸エステル sanofi chimie 82 avenue raspail 94250 gentilly france NaN NaN False False False
... ... ... ... ... ... ... ... ... ... ... ... ...
2633 219MF10105 2007/03/23 2012/03/30 医薬品等原薬 イトラコナゾール mylan laboratories limited plot no.564/a/22, road no.92, jubilee hills, h... NaN NaN False False False
2821 222MF10253 2010/10/28 2010/10/28 医薬品等原薬 オロパタジン塩酸塩 kolon life science,inc. 110,magokdong-ro,gangseo-gu,seoul,korea NaN NaN False False False
3478 217MF10151 2005/05/31 2008/08/18 医薬品等原薬 マレイン酸セチプチリン「サンヨー」 株式会社 三洋化学研究所 大阪府堺市美原区多治井148-1 NaN NaN False False False
3793 219MF10159 2007/05/15 2007/05/15 医薬品等原薬 チクロピジン塩酸塩「テバ」 sicor societa(`) italiana corticosteroidi s.r.l. piazzale luigi cadorna, 4 - 20123 milano, italy NaN NaN False False False
3809 218MF10270 2006/02/17 2007/04/27 医薬品等原薬 日本薬局方 メキタジン 住友化学株式会社 東京都中央区日本橋二丁目7番1号 NaN NaN False False False
1129 rows × 12 columns
"""
最後に、こちらを参考にして合成難易度 (sa)を計算しました。MolObjectを受け取ってsaを返す関数rtn_sa
を作成しました。
def rtn_sa(mol):
if mol==False:
return False
sa = sascorer.calculateScore(mol)
return sa
df_test.loc[:,'sa'] = df_test['ROMol'].map(rtn_sa)
df_test
"""
MF登録番号 初回登録年月日 最新登録年月日 登録区分 登録品目名 登録者氏名 登録者住所 原薬等国内管理人の名称 原薬等国内管理人の住所 cas smiles ROMol sa
26 219MF10293 2007/09/10 2019/03/29 医薬品等原薬 スルバクタムナトリウム(製造専用) kyongbo pharmaceutical co.,ltd. 174,sirok-ro,asan-si,chungcheongnam-do,korea NaN NaN 69388-84-7 CC1(C(N2C(S1(=O)=O)CC2=O)C(=O)[O-])C.[Na+] <img data-content="rdkit/molecule" src="data:i... 4.448697
28 218MF10710 2006/08/14 2019/03/29 医薬品等原薬 日本薬局方 スルバクタムナトリウム(製造専用) kyongbo pharmaceutical co.,ltd. 174,sirok-ro,asan-si,chungcheongnam-do,korea NaN NaN False False False False
36 218MF10971 2006/12/01 2019/03/19 医薬品等原薬 ナロキソン塩酸塩水和物 sanofi chimie 82 avenue raspail 94250 gentilly france NaN NaN 51481-60-8 C=CCN1CCC23C4C(=O)CCC2(C1CC5=C3C(=C(C=C5)O)O4)... <img data-content="rdkit/molecule" src="data:i... 5.410687
42 230MF10133 2018/09/14 2019/03/12 医薬品等原薬 メチルプレドニゾロンコハク酸エステルナトリウム sanofi chimie 82 avenue raspail 94250 gentilly france NaN NaN 2375-03-3 CC1CC2C3CCC(C3(CC(C2C4(C1=CC(=O)C=C4)C)O)C)(C(... <img data-content="rdkit/molecule" src="data:i... 5.00208
52 220MF10128 2008/05/15 2019/03/05 医薬品等原薬 メチルプレドニゾロンコハク酸エステル sanofi chimie 82 avenue raspail 94250 gentilly france NaN NaN False False False False
... ... ... ... ... ... ... ... ... ... ... ... ... ...
2633 219MF10105 2007/03/23 2012/03/30 医薬品等原薬 イトラコナゾール mylan laboratories limited plot no.564/a/22, road no.92, jubilee hills, h... NaN NaN False False False False
2821 222MF10253 2010/10/28 2010/10/28 医薬品等原薬 オロパタジン塩酸塩 kolon life science,inc. 110,magokdong-ro,gangseo-gu,seoul,korea NaN NaN False False False False
3478 217MF10151 2005/05/31 2008/08/18 医薬品等原薬 マレイン酸セチプチリン「サンヨー」 株式会社 三洋化学研究所 大阪府堺市美原区多治井148-1 NaN NaN False False False False
3793 219MF10159 2007/05/15 2007/05/15 医薬品等原薬 チクロピジン塩酸塩「テバ」 sicor societa(`) italiana corticosteroidi s.r.l. piazzale luigi cadorna, 4 - 20123 milano, italy NaN NaN False False False False
3809 218MF10270 2006/02/17 2007/04/27 医薬品等原薬 日本薬局方 メキタジン 住友化学株式会社 東京都中央区日本橋二丁目7番1号 NaN NaN False False False False
1129 rows × 13 columns
"""
データ分析するために前処理を行います。saが計算できなかったものは除外しました。Top20で抽出しましたが、処理の過程で一社欠損してしまったようでした。
df_ana = df_test[df_test['sa']!=False]
print(df_ana.shape)
print(len(df_ana['登録者氏名'].unique()))
"""
(408, 13)
19
"""
saについて統計量を計算しました。平均値についてランキングして整理した結果が以下の通りです。社名に\u3000
が入り込んでいるのは気にしないでください。
df_rank = df_ana.groupby(['登録者氏名'])['sa'].agg(['mean', 'std', 'count', 'max', 'min']).sort_values(by="mean", ascending=False)
df_rank
mean | std | count | max | min | |
---|---|---|---|---|---|
登録者氏名 | |||||
sanofi\u3000chimie | 4.791725 | 1.956721 | 22 | 8.378029 | 2.243329 |
アルプス薬品工業株式会社 | 4.613620 | 0.906464 | 15 | 5.929040 | 2.869499 |
hanmi\u3000fine\u3000chemical\u3000co.,ltd. | 3.917504 | 1.021601 | 14 | 6.116182 | 2.505240 |
kyongbo\u3000pharmaceutical\u3000co.,ltd. | 3.655462 | 1.243465 | 39 | 8.256955 | 1.871614 |
kolon\u3000life\u3000science,inc. | 3.620023 | 0.987406 | 16 | 5.361381 | 2.492740 |
sicor\u3000societa(`)\u3000italiana\u3000corticosteroidi\u3000s.r.l. | 3.347663 | 1.118844 | 4 | 4.828636 | 2.122733 |
hetero\u3000labs\u3000limited | 3.278368 | 0.845611 | 22 | 5.326849 | 2.337358 |
mylan\u3000laboratories\u3000limited | 3.259816 | 0.926031 | 32 | 6.116182 | 2.251534 |
相模化成工業株式会社 | 3.246741 | 1.018134 | 13 | 5.086630 | 2.116077 |
aurobindo\u3000pharma\u3000limited | 3.207911 | 0.735930 | 19 | 4.445804 | 2.343852 |
株式会社\u3000大阪合成有機化学研究所 | 3.205708 | 1.046548 | 12 | 5.629029 | 2.257189 |
olon\u3000s.p.a. | 3.119780 | 0.753195 | 24 | 5.625602 | 2.275611 |
ダイト株式会社 | 3.053274 | 0.791519 | 30 | 5.361381 | 2.097384 |
株式会社\u3000三洋化学研究所 | 3.007404 | 0.630739 | 41 | 5.361381 | 2.116077 |
株式会社パーマケム・アジア | 2.914877 | 0.701407 | 31 | 4.794299 | 1.469664 |
cambrex\u3000profarmaco\u3000milano\u3000s.r.l. | 2.850941 | 1.009620 | 23 | 7.173955 | 2.113785 |
住友化学株式会社 | 2.741267 | 0.498912 | 19 | 3.580353 | 1.811313 |
大和薬品工業株式会社 | 2.733274 | 0.440762 | 11 | 3.342106 | 1.819255 |
八代製薬株式会社 | 2.590104 | 0.610828 | 21 | 3.705913 | 1.407299 |
Top20中のsaの分布を以下に示します。sa=3を境にグッと分布が少なくなっています。
plt.grid()
plt.hist(df_ana['sa'], bins=20)
plt.xlim(0,10)
plt.xlabel('sa')
plt.ylabel('count')
plt.xticks(np.arange(0,10,1))
plt.title('Distribution of total sa')
最後に、企業別のsaの分布を以下に示します。タイトルが長いと別のグラフに被ってしまうので、ある程度の長さで切っています。
fig, axes = plt.subplots(4,5, figsize=(15,15))
for com, ax in zip(df_ana['登録者氏名'].unique(), axes.ravel()):
df_tmp = df_ana[df_ana['登録者氏名']==com]
ax.hist(df_tmp['sa'].astype(float).round(2), bins=10)
ax.set_xlim(0,10)
ax.set_ylim(0,12)
ax.set_xlabel('sa')
ax.set_ylabel('count')
ax.set_xticks(np.arange(0,10,1))
ax.set_yticks(np.arange(0,12,1))
if len(com)>=15:
com = com[:15]
ax.set_title(com)
ax.grid()
fig.tight_layout()
考察
今回の調査で得た考察を以下に示します。
- 全てのデータからTop20の企業を抽出すると、1129件であった。そこから、登録品目名+casというキーワードでgoogle検索してcas番号を正規表現で取り出した。検索画面からcas番号を見つけ出すため、見逃しが多少あった。cas番号からsmilesの取得をpubchemで行った。この検索もヒットしない場合もあったため、最終的には408件のデータを抽出した。
- Synthetic Accessibility score (sa:高いほど合成難易度が高い)について、企業ごとに基本統計量 (平均、標準偏差、個数、最大値、最小値)を求めた。sicor societaはn=4と最もエントリー数が少なかったが、その他のエントリー数は10個以上であったので、ある程度意味のある比較が可能であると判断した。
- 全体のsaの分布を確認すると、およそ3が最頻値であり、それ以上の値は極端に少なくなっていたことから、sa=3が一つの指標となる。
- saの平均値でランキングしたところ、1位はsanofiであり、最大値も1位であった。エントリー数も22個であり、sa=8の化合物が4件もあることから、間違いなく技術力が高い企業であることが読み取れる。
- MF申請数ランキング実質1位であった三洋化学研究所は、saランキングでは14位となっており、あまりチャレンジせずに手堅く実績を残している様子であった。
- MF申請数ランキング実質3位であったアルプス薬品工業は、saランキングでも2位と海外勢を抑えて上位に位置しており、質と量ともにレベルの高さがうかがえる。標準偏差も0.9と常に挑戦的な研究開発に取り組んでいると考えられる。
- MF申請数ランキング実質5位であったダイトは、saランキングでは13位となっており、あまりチャレンジせずに手堅く実績を残している様子であった。
- 上位の半数以上が海外勢であり、上記のバブルチャートでも述べた通り、研究開発は世界レベルのものが求められている。
所感
今回のデータ分析の結果から、国内の競合他社は三洋化学研究所、アルプス薬品工業、ダイトであることが分かりました。国外からの参入もあり、勝ち残っていくには世界レベルの研究開発が必要であることも分かりました。
コード技術の観点からでは、表記されている化合物から合成難易度を算出して、企業の研究開発レベルを定量化する方法は我ながらよく思いついたなと感じました。企業情報をさらに付け加えたいと思ったのですが、企業データベースは基本有料だったので、止む無く断念しました。
今後の予定
医薬品業界を知る情報源はまだまだありますので、今後も情報発信をしていきたいと思います。
最後まで読んでいただきありがとうございました。
参考文献
【特許分析】Pythonでお金を掛けずにパテントマップを作ってみた
RDKitで合成難易度を評価して化合物をスクリーニング (化学の新しいカタチ)