cross_entropy_error

#ロードバイク #山形

GoogleSpeechAPIにより.wavファイルの文字起こしを行うPythonスクリプトプログラム

 

 

pythonでPDFを操作する場合には、PyMuPDFがいいんでない?という話

pythonでPDFを操作したい

pythondjango)で会議用の管理システムを開発したのですが、最も実装したかった機能が資料のPDFを結合したり、通し番号を入れたりする機能でした。

pythonのPDF用ライブラリを検索すると、よく出てくるのがPyPDF2やpdfrwですが、今回のシステムで使用したのがPyMuPDFです。

当初はPyPDF2、pdfrwを使用したコードを書いたのですが、PDFに組み込まれているJavaScriptがどうしても処理上の邪魔になったりで思うようにバグが消せなかったところで、そもそも各々のPDFファイルに組み込まれたJavaScriptをはじめに削除することができるPyMuPDFを使用することで一気に問題が解消しました。

PDFを操作する際の問題点

今回実装したPDFの操作機能には、主に以下の前提条件が与えられます。

  • 資料となるPDFはあらゆる人間があらゆるソフト(またはスキャン)で作成しアップロードしてくる。
  • 資料のPDFは様々な大きさ、アスペクトが与えられている。
  • さらに、JavaScriptによって印刷条件や表示時の回転処理等が与えられている。

上記の前提条件を踏まえたうえで、任意のPDFファイルを正しく結合させる必要があります。

PyPDF2やpdfrwでの実装

初めにPyPDF2とpdfrwを使用する実装を検討します。この方法ではどちらか一方のライブラリで完結することがまずできません。ネット上を検索すると出てくる方法としては、

  1. PyPDF2で各PDFファイルを結合する。
  2. 結合したPDFファイルを再度読み込み、pdfrwのcanvasで各ページのmediaboxと同一の空白ページを1枚ずつ順番に生成し、もともとの該当ページの内容を転記、さらに追記したい内容(今回は通しページ番号)をさらに追記する工程をループしていく。

という方法です。

この方法は規格が決まったPDFファイルではうまくいきますが、回転が加わっていたり、印刷用のJavaScriptが組み込まれているようなPDFファイルではうまくできません。(たとえば、回転角を事前に取得してページ番号を挿入位置や文字の回転角度を逆算して場合分けしてやる必要性があったりコードも煩雑になります。)

当然、様々なPDFファイルが混在する環境では望んだ結果が得られません。

.scrub()を使用してPDFの処理前に事前にJavaScriptを削除する。

上記の問題はほとんどがJavaScriptが望んでいない挙動を起こすことから生まれますので、とにかく対象とするPDFを結合する処理を行う前に、余計な情報を削除してやる必要がありますが、PyMuPDFでは

import fitz

idoc =  fitz.open(file_name)
idoc.scrub()

基本的にこれだけで事足ります。.scrub()で余計なJavaScriptを削除したら、あとは公式ドキュメントどおりに.insertText()メソッドで

target_x, target_y = media_wsize/2,media_hsize-20
p = fitz.Point(target_x,target_y)
rc = read_odoc.insertText(
        p,  # bottom-left of 1st char
        insert_text,  # the text (honors '\n')
        fontname="helv",  # the default font
        fontsize=14,  # the default font size
        rotate=0,  # also available: 90, 180, 270
        color=(0,0,0),
        )

書き足したい文字を挿入するだけです。

PyMuPDFのドキュメントについて

PyMuPDFの情報については日本語ではほとんど得られなかったので、公式ドキュメントを読むのが一番いいと思います。

PyMuPDF Documentation

GoldenCheetah OpenDataを使って到達パワーを回帰予測してみる

ちょっと真面目にトレーニングをしているサイクリストなら、一度は使ったことがあるであろう「Golden Cheetah」プロジェクトが公開しているトレーニーのオープンデータを使用して、トレーニングによって期待できる最大パワーを予測してみました。という話です。

GoldenCheetah OpenDataとは

github.com

まぁ、詳しくは私も知らないので、(今回も結構趣味の遊びの範囲を出ませんし)githubを読んでみてほしいのですが、goldencheetahを使用しているトーレーニーの人たち(サイクリストだけでなく、トライアスリートも多めです。)のトレーニングデータがcsv形式で公開されているものです。 データは、各アクティビティデータもありますし、トレーニーごとのクリティカルパワー、体重なんかのプロフィールデータもあります。それぞれは個別IDで付番されているので、データの結合も容易でした。

20分間のクリティカルパワーを予測する回帰モデル

一昔前であれば、ローラー台でやるメニューと言えば20分走でした。個人的に興味のあるこの領域を予測できるモデルを目指します。 使用したデータは、GC OpenDataのathletes.csvです。このデータに入っているカラムはこんな感じでした。

import pandas as pd
athletes = pd.read_csv("athletes.csv")
print(athletes.columns)

Index(['id', 'age', 'gender', 'activities', 'bike', 'run', 'swim', 'other',
       '1s_critical_power', '15s_critical_power', '2m_critical_power',
       '3m_critical_power', '5m_critical_power', '8m_critical_power',
       '10m_critical_power', '20m_critical_power', '30m_critical_power',
       '1m_peak_wpk', '5m_peak_wpk', '10m_peak_wpk', '20m_peak_wpk',
       '30m_peak_wpk', '20s_peak', '20s_peak_wpk', '60s_peak', '60s_peak_wpk',
       '180s_peak', '180s_peak_wpk', '240s_peak', '240s_peak_wpk', '420s_peak',
       '420s_peak_wpk', '720s_peak', '720s_peak_wpk', 'weightkg',
       ' weightstd'],
      dtype='object')

***_critical_power 以降のデータはラン、スイムの指標なので、今回はあまり使わなくても良さそうです。逆に"weightkg"や"age"は経験則上かなり相関がありそうですが、各変数の寄与具合も気になるところです。

ここから、このデータをクレンジングする作業が続くわけですが、あくまでも個人的興味に基づく分析なので、年齢は18歳から60歳、体重は40kgから100kg、パワーも大きな外れ値(2000Wとか)は.loc[]で外していきました。 そんなこんなで解析にかけるデータ(具材)をちまちま用意して、scikit-learnで回帰予測をしてみます。

from sklearn import  linear_model
import sklearn.model_selection
model = linear_model.LinearRegression()
X = predict_data_rules[["age","bike","gender","5m_critical_power","10m_critical_power","weightkg"]]
y = predict_data_rules["20m_critical_power"]
X_train,X_test,y_train,y_test = sklearn.model_selection.train_test_split(X,y)
model.fit(X_train,y_train)
rider = [30,600,1,300,300,70] #年齢、練習回数、男女、5分最大パワー、10分最大パワー、体重から、20分最大パワーを回帰予測する
pred = [rider]
print("期待できる20分最大パワー:", model.predict(pred).round(2))

期待できる20分最大パワー: [292.89]

10分のクリティカルパワーの寄与が大きすぎるので、月の練習時間や距離なんかも加えるともっと良いモデルになると思います。

それにしても、この設定値はおおよそ私のベストな数値を入れてみたのですが、出力された期待できるパワーが292Wというのは、確かにこれまで調子いいときに出せてたくらいの数値です。

もう少し解析するデータを整えれば、トレーニングを行う上での改善指標が明確にできるかもしれませんね。