ipynbファイル内のPythonコード行数のカウント

はじめに

突然,「卒論でどのくらいコードを書いたんだろう?」と気になり,調べてみた.卒論で行ったデータ解析は,前処理,解析,可視化等をすべてPythonのnotebook(.ipynb)で書いている.そのため,作業用ディレクトリの中の全.ipynbファイルを調べて,コードの総行数をカウントすればよい.

以上より,本記事では,任意のディレクトリ配下の.ipynbファイルのコード総数を調べる方法を簡単にまとめる.

ipynbの仕様

ipynbファイルはjson記法のテキストファイルである.これをjupyter notebookやjupyterLabで開くことで,お馴染みの作業環境が実現する.

しかし,ipynbファイル自体はメタ情報も含まれた(json likeな)テキストファイルであるので,
そのファイル行数を確認しても自分で書いたpythonコードの行数にはならない.

実際に,テキストエディタ等でipynbファイルを開くと,json記法となっていることが確認できる.
例えば,このような作業環境に対応する部分は



以下のようなデータ形式で保存されている.

{'cells': [{'cell_type': 'code',
   'execution_count': 4,
   'metadata': {},
   'outputs': [],
   'source': ['import numpy as np\n',
    'import pandas as pd\n',
    'import json as json']},
  {'cell_type': 'code',
   'execution_count': 5,
   'metadata': {},
   'outputs': [],
   'source': ["f = open('20190411_RC_base_link_table.json', encoding='utf-8-sig')\n",
    'Link_dic = json.load(f)\n',
    'f.close()\n',
    '\n',
    "f = open('20190516_RC_base_node_table.json', encoding='utf-8-sig')\n",
    'Node_dic = json.load(f)\n',
    'f.close()']},
   {'cell_type': 'code',
   'execution_count': 20,
   'metadata': {},
   'outputs': [{'data': {'text/plain': ["{'def_x_pos': 31,\n",
       " 'def_y_pos': 15,\n",
       " 'in_kanchiki_id_list': ['80-01-01', '00-01-08'],\n",
       " 'in_link_list': ['498_505'],\n",
       " 'node_attribute': 'O',\n",
       " 'out_kanchiki_id_list': '0',\n",
       " 'out_link_list': ['505_506']}"]},
     'execution_count': 20,
     'metadata': {},
     'output_type': 'execute_result'}],
   'source': ["Node_dic['505']"]},
   .
   .
   .
 }

詳細な仕様は,The Notebook file format — nbformat 4.5 documentation
にある.
今回は,セルごとのブロック中で,
sourceキーに対応するアイテムが,実際に書いたpythonコードに該当することがわかれば良い.
リスト形式になっており,リストの各要素と行が対応している.
(markdown セルは,cell_typeが`markdown’となる.)

コード

In [1]:
# jsonデータをpythonの辞書で扱うために必要
import json

# ファイル操作に必要
import glob
In [2]:
# ひとつの.ipynbファイルのpythonコード行数をカウントする関数
def Count_Lines(file_path):
    
    # ファイルを開き,辞書としてpythonの変数に流す
    f = open(file_path, encoding='utf-8-sig')
    ipynb_dict = json.load(f)
    f.close()
    
    Num_lines = 0
    for cell_data in ipynb_dict['cells']:
        
        # 各セルのpythonコード行数(=sourceリストの要素数)をカウント
        if cell_data['cell_type'] == 'code':
            Num_lines = Num_lines + len(cell_data['source'])
                     
    return Num_lines
In [3]:
# 動作の確認
file_path = '..\\20191108\\cheack.ipynb'
Count_Lines(file_path)
Out[3]:
13
In [4]:
# XXXディレクトリ配下の.ipynbファイルのパスリストを取得

path = 'XXX//**//*.ipynb'


file_path_list = glob.glob(path, recursive=True)
In [5]:
# 全ファイルに対してpythonコード行数をカウント

data = {}
N = 0
for file_path in file_path_list:
    Lines = Count_Lines(file_path)
    
    data[file_path] = Lines
    N = N + Lines
In [6]:
# 結果の確認
print('Number of files : ', len(file_path_list))
print('Number of lines : ', N)
Number of files :  140
Number of lines :  18231

おわりに

上記の結果は,実際の卒論作業ディレクトリに対して実施している.
ということで,1万8千行ほど書いたらしい.思ったより,少なく残念であった.

1