brk-code

PageRank で公開 API を選ぶと失敗する — 実測で分かった 4 つの誤り

コード知識ベースを圧縮する際、重要度スコアで残す API を選ぶ手法は失敗しました。pandas の groupby が丸ごと消えた原因と、保護層による解決を実測データとともに解説します。

公開日: 2026-08-07 静的解析圧縮ランキングPageRank

コードの知識ベースを圧縮するとき、避けて通れない問題があります。 全部は入らないので、何かを捨てなければならない。

では何を捨てるか。素直に考えれば「重要でないもの」です。そして重要度を測る 定番の手法が PageRank です。呼び出しグラフ上でよく参照されるシンボルほど 重要である——直感的で、実装も簡単です。

これは失敗しました。しかも 4 回連続で。

症状: pandas の groupby が消えた

901 のオープンソースリポジトリから抽出した 145 万シンボルを圧縮した結果、 DataFrameGroupBy.aggregate が消えていました。Series.groupby も、 SeriesGroupBy.aggregate もです。

pandas を使う人が最も知りたい API が、丸ごと失われていたことになります。 一方で生き残っていたのは、こういうものでした。

pandas.generate_pxi.main
pandas.generate_version.write_version_info
pandas._config.config.get_default_val

ビルドスクリプトと内部設定です。利用者が呼ぶことのないコードが優先的に 残り、主力 API が捨てられていました。

原因: PageRank は「呼ばれる回数」しか測らない

理由は考えてみれば当然でした。

PageRank はグラフ上で「どれだけ参照されるか」を測ります。しかし 公開 API のエンドポイントは、ライブラリ内部からはほとんど呼ばれません。 呼ぶのは外部の利用者コードだからです。そして利用者コードは解析対象に 含まれていません。

実測してみると、決定的な数字が出ました。

シンボルPageRank
DataFrameGroupBy.aggregate3.37e-05
生き残った平凡なメソッド3.37e-05
Series クラス桁が 2 つ上

3.37e-05 は孤立ノードの最低値です。つまり pandas の大半のシンボルは PageRank 上で区別がついていませんでした。 順位は事実上、推定コストの 端数で決まっていたのです。意味のない順序でした。

失敗した 4 回の試行

最初は重み付けの調整で解けると考えました。公開 API にボーナスを与え、 docstring があれば加点し、型注釈があればさらに加点する。

結果は以下の通りです。

試行手法API 保持率
開始時PageRank のみ90.3%
1 回目公開・doc・型注釈にボーナス81%
2 回目グラフ価値と API 価値の max を取る81%
3 回目加算方式に変更81%
4 回目コスト関数を調整77.4%

触るたびに悪化しました。 これは重み調整という手法そのものが 間違っている証拠です。

途中で分かった細かい罠も記録しておきます。docstring の「長さ」を 品質の指標にしたところ、かえって精度が落ちました。pandas の docstring は 一文目が簡潔で、"Aggregate using one or more operations." はわずか 39 文字です。 長さで測ると、丁寧に書かれた優良な docstring ほど罰されるという 逆転が起きていました。

解決: 順位付けをやめ、保護層を作る

発想を変えました。順位付けが縮退している領域で順序をいじっても、 重要な API が残る保証は永久に得られません。必要なのは順位ではなく 保証です。

そこで刈り取りの母集団から外すことにしました。

def _is_surface_api(self, s):
    """予算に関わらず最後まで守る対象か"""
    if not s.public:
        return False
    if not (s.gist or s.doc):
        return False          # docstring 無しは表 API とみなさない
    if s.name.startswith("__") and s.name.endswith("__"):
        return False          # dunder は鋳型で復元できる
    if set(s.qual.split(".")) & _NON_API_MODULES:
        return False          # conftest 等のテスト補助は除外
    return True

守る条件はシンプルです。公開されていて、docstring があるもの。 これは「作者が意図して利用者に見せている API」の定義とほぼ一致します。

結果は一発で解決しました。

指標修正前修正後
API 保持率90.3%100%
ノイズ混入84 件0 件

10 ライブラリ 31 個の代表 API すべてが、圧縮後も残るようになりました。 sqlalchemy は 9,902 シンボルを 2,220 まで削っても select / join / Column が無傷です。

教訓

PageRank が縮退している領域では、順位付けではなく「何を守るか」を 決めるほうが正しい。

これは一般化できる話だと考えています。スコアで並べる手法は、スコアが 意味のある差を持つ範囲でしか機能しません。差がない集団を並べ替えても、 出てくるのはノイズの順序です。

そしてもう一つ。測定手段を先に作るべきでした。 4 回の失敗のうち 最初の 3 回は、そもそも改善しているか悪化しているか分からないまま 進めていました。回帰テストを書いてから、初めて「触るたびに悪化している」 という事実が見えたのです。

補足: 触ってはいけない定数

同じ罠を踏まないよう、実装には注意書きを残しています。

巻き込み、保持率が 100% から 90% に落ちます。

罰されます。

圧縮率を上げるだけなら簡単です。全部捨てれば 0 バイトになります。 難しいのは、利用者が実際に知りたいものを残すことです。

関連する記事

Python のソース 2.0GB を 4.75MB にする — 圧縮ではなく「表現の変換」2026-08-09AI が API の引数を間違える理由 — get() が 678 通り存在するという事実2026-08-08

記事一覧へ戻る