Pythonのpandasはデータの解析に有用なツールである。今回は、国際特許分類(IPC)の整理をpandasで行い、特許解析に役立てたい。前回は、特許解析に役立つ国際特許分類(IPC)について、まとめてみました。今回は、特許庁のwebサイトからダウンロードしたIPCのExcelファイルのデータをpandasで加工して、利用可能な関数するための、行の分類までを行います。
ExcelファイルからのIPCデータ抽出
Excelファイルの加工はインタラクティブなPython開発環境のJupyterLabを用いて行います。Python環境構築については、過去の記事(AnacondaをアンインストールしてPythonのパッケージからインストール)の後半部分をご参考ください。作業するディレクトリの中に、「data」ディレクトリを作成し、その中に、特許庁のwebサイトからダウンロードし、解凍したExcelファイルを入れておきます。
今回はその中で、Hセクションのデータ「IPC_ver2022-Hsection.xls」を使って、作業してみます。まずは、ターミナルから、必要なライブラリをダウンロードします。今回は、以下のライブラリを使用します。インストールしていない場合は、「python -m pip install [ライブラリ名]」でインストールします。
- jupyterlab
- pandas
- openpyxl
- xlrd
python -m pip install jupyterlab
python -m pip install pandas
python -m pip install openpyxl
python -m pip install xlrd
JupyterLabはwebブラウザで動作するインタラクティブなPython開発環境です。JupyterLabの日本語化など、詳しい導入方法は、以前の記事をご参考ください。pandasはpythonでデータ解析をするのに非常に便利なライブラリです。openpyxlとxlrdはpandas内でExcelファイルを扱うときにバックグラウンドで必要になります。
それでは、JupyterLabを起動します。
jupyter lab
起動したら、NotebookのPython3を選択し、jupyter対話環境を起動します。
これ以下のPythonプログラムはすべて対話環境で作成したものです。
IPCデータの読み込み
まず、pandasとnumpyをインポートしてきて、pandasのread_excelでIPCデータのDataFrameを作成します。あと、確認用にset_optionで表示できる行数を50行にしておきます。今回はIPCデータはdataディレクトリの下の”IPC_Ver2022-Hsection.xls”というファイルなので、そこを読みにいきましたが、お自分がダウンロードしたディレクトリや保存したファイル名を指定するようにしてください。
# 使うライブラリをインポート
import pandas as pd
import numpy as np
# ローカル環境に保存し、解凍したIPCのExcelファイルをread_excelでDataFrame形式で読み込む
df = pd.read_excel("./data/IPC_Ver2022-Hsection.xls")
# 対話型開発環境で表示できる行数(デフォルトでは5行)を50行に増やす
pd.set_option('display.max_rows', 50)
それでは、作成したDataFrameを確認してみます。
# 対話型環境では、各カラムの最終行の変数は中身を表示してくれる。print(df)と書くのとほぼ同義。
df
インデキシングフラグ | 記号 | ドット | タイトル | |
---|---|---|---|---|
0 | NaN | H | NaN | 電気 |
1 | NaN | <注> | NaN | (利用にあたっての基本原則と一般的指示) |
2 | NaN | NaN | NaN | I.セクションHは以下のものを包含する: |
3 | NaN | NaN | NaN | (a)基本的電気素子,これは全ての電気ユニットと,装置や回路の一般的な機械的構成を包含してお… |
4 | NaN | NaN | NaN | (b)発電,これは,発電,変換および配電と共にそのための装置の制御を包含する; |
… | … | … | … | … |
10823 | NaN | H99Z | NaN | このセクションの中で他に分類されない主題事項[8] |
10824 | NaN | <注> | NaN | このサブクラスは,以下の主題事項を包含する:[8] |
10825 | NaN | NaN | NaN | (a)このセクションのサブクラスに包含される主題事項に分類されないが,これに最も関連している… |
10826 | NaN | NaN | NaN | (b)別のセクションの何れのサブクラスにも明示的に包含されないもの[8] |
10827 | NaN | H99Z 99/00 | NaN | このセクションの中で他に分類されない主題事項[8] |
10828 rows × 4 columns
DataFrameには「インデキシングフラグ」、「記号」、「ドット」、「タイトル」の列がありますが、そのうち、「インデキシングフラグ」は直接IPCの階層構造とは関係なさそうなので、削除します。「ドット」の部分はサブグループの階層深さを示すものですが、ここでは使わないものの、今後、使うかもしれないので残しておきます。
列の削除はdrop文で列名を指定して、axis=1とします。(デフォルトはaxis=0で行方向に削除)
# インデキシングフラグ列を削除
df = df.drop("インデキシングフラグ",axis=1)
各行の分類
行の中には、IPCの階層と関係ない索引や注釈の書いてある行があるので、その行は削除が必要です。また、それぞれ、セクション、クラス、サブクラス、メイングループ、サブグループなど異なる階層の情報が一律に並んでいるので、それぞれの行が何を示すか判断しないといけません。
ここでは、「level」列を追加して、それぞれの行が何を示すか確認します。ここでは、新たな関数”level_checker”を定義してみます。IPCのルールから、先頭文字は必ずA~Hのアルファベットから始まることがわかっています。ここでは、正規表現のmatchコマンドを使って、確認していきたいと思います。まずは練習で、その先頭文字かどうかを確認する関数を作ってみます。正規表現を使う場合はpythonに標準で入っているreをインポートします。続いて、下記のコマンドで、IPC_codeの文字列の先頭がA~Hで始まる場合はTrueが返ります。[A-H]はA~Hの文字という意味で、それとマッチしているかを確認しています。
re.match(‘[A-H]’, IPC_code)
# level列を追加
# level :表現形式:判断ロジック
# level = 0 : 不要な行:先頭がA~Hのアルファベットではない
# level = 1 : セクション(アルファベット1文字):A~Hのアルファベット
# level = 2 : クラス(アルファベット1文字+数字2桁:3桁
# level = 3 : サブクラス(アルファベット1文字+数字1~2桁+アルファベット1文字):4桁
# level = 4 : メイングループ(アルファベット1文字+数字2桁+アルファベット1文字+スペース+数字1~2桁+スラッシュ+00):スラッシュを含み、スラッシュの右が00
# level = 5 : サブグループ(アルファベット1文字+数字2桁+アルファベット1文字+スペース+数字1~2桁+スラッシュ+00以外の数字):スラッシュを含み、スラッシュの右が00以外
# 正規表現を使うためにreをインポート
import re
def level_checker(IPC_code):
if re.match('[A-H]', IPC_code):
return True
else:
return False
# 作成したlevel_checker関数が使えるか確認
for IPC_code in ['B', '', '<注>','B12G 12/23', 'd12', 'H12G', 'E11A 10/00']:
print(level_checker(IPC_code))
True False False True False True True
ちゃんと判定できています。
続いて、level_checker関数を完成させていきます。ここではlevel_checkerに入力された値をIPCの記述ルールに則って、どの階層であるかチェックし、その階層に応じてlevel=1~5のラベルを付けます。ただし、IPCのルール外だった場合は、level=0を返します。
条件①・・・入力値がフロートだった場合はlevel=0(空白行対策)
条件②・・・入力値がA~Hのアルファベットで始まり、
条件②’・・・1文字 →セクション(level=1)
条件②”・・・3文字 →クラス(level=2)
条件②”’・・・4文字 →サブクラス(level=3)
条件②””・・・スラッシュ(“/”)でスライスた右の2桁の数字が00→メイングループ(level=4)
条件②””’・・・それ以外→サブグループ(level=5)
入力値がA~Hで始まらない場合はlevel=0
あまり美しくないコードですが、ここは力業で判定します。最後に作成したlevel_checker関数を7種類のダミーコードで確認し、正しく判定できていることを確認しました。
# 正規表現をつかうためにreをインポート
import re
def level_checker(IPC_code):
if type(IPC_code) is float:
return 0
if re.match('[A-H]', IPC_code):
if len(IPC_code) == 1:
return 1
elif len(IPC_code) == 3:
return 2
elif len(IPC_code) == 4:
return 3
elif IPC_code.split('/')[1] == '00':
return 4
else:
return 5
else:
return 0
for IPC_code in ['B', '', '<注>','B12G 12/23', 'd12', 'H12G', 'E11A 10/00']:
print(level_checker(IPC_code))
1 0 0 5 0 3 4
それでは、各行の”記号”列から値を読み取り、level_checker関数を適用させて、新な”level”列を作成します。pandasのapplyコマンドを使うと簡単に関数を適用できます。
df['level'] = df['記号'].apply(level_checker)
それでは”level”列から、どのように判定されたか確認してみます。
print(df['level'].value_counts())
print('sum= ' + str(df['level'].value_counts().sum()))
5 9409 0 802 4 559 3 51 2 6 1 1 Name: level, dtype: int64 sum= 10828
全行数10828に対して、サブグループが9409行で、不要な行が802行、メイングループが559行、サブクラスが51行、クラスが6行、セクションが1行であることがわかりました。
それではlevel=0の行のインデックスを配列”index_nums”に取得して、その行を削除します。
index_nums = df[df['level']== 0].index
df.drop(index_nums, inplace = True)
中身を見てみます。
df
記号 | ドット | タイトル | level | |
---|---|---|---|---|
0 | H | NaN | 電気 | 1 |
23 | H01 | NaN | 基本的電気素子 | 2 |
26 | H01B | NaN | ケーブル;導体;絶縁体;導電性,絶縁性または誘導性特性に対する材料の選択(磁気特性に対する選… | 3 |
36 | H01B 1/00 | NaN | 導電材料によって特徴づけられる導体または導電物体;導体としての材料の選択(材料によって特徴づ… | 4 |
38 | H01B 1/02 | ・ | 主として金属または合金からなるもの | 5 |
… | … | … | … | … |
10820 | H05K 13/06 | ・ | 機械による配線[2006.01] | 5 |
10821 | H05K 13/08 | ・ | 組立体の製造の監視[2006.01] | 5 |
10822 | H99 | NaN | このセクションの中で他に分類されない主題事項[8] | 2 |
10823 | H99Z | NaN | このセクションの中で他に分類されない主題事項[8] | 3 |
10827 | H99Z 99/00 | NaN | このセクションの中で他に分類されない主題事項[8] | 4 |
10026 rows × 4 columns
特許庁からダウンロードしたExcelファイルの中のIPCを整理できました。
コメント