めたへいログ

GIS系エンジニアの備忘録

Kindle端末上のmobiファイルで引いたハイライトをcsv形式に出力するプログラムを作ってみた

寝る前にベッドでKindle Paperwhiteを開き、一日の終わりに読書をするのが習慣として根付いている。 少しずつでも読み進めて、琴線に触れた部分をハイライトして、後で見返すのも面白い。

Kindleの便利な点として、mobi形式であれば、Amazonで購入したもの以外でも読んでハイライトができる点にある。 ファイルの転送方法なんかも比較的簡単に紹介されており、実際にこの機能はよく利用する。

www.kotorisha.co.jp

海外の著作権切れの書籍などはmobi形式で提供されていることも多く、また、pdf形式やepub形式のファイルをmobiに変換する方法なども調べると出てくるので、利用範囲はとても広い。

www.gutenberg.org

ただ、問題点として、Amazonで購入した書籍であればハイライトをエクスポートすることも簡単にできるものの、端末に直接保存しているmobiファイルではこのエクスポートが効いてくれない。 この問題点を解決したい。

ハイライト自体はテキストで保存されているらしい

調べてみると、ハイライトした部分自体はMy clippings.txtというファイルで管理されているらしい。

kensawai.com

体裁を見るとデリミタも固定である様子。Webサービスとしてclippings.ioのようなものもあるようだが、そこまで凝ったことがしたいわけでもない。 単純に情報をcsvで吸い出して、Excelでまとめて見れれば正直やりたいことはできる。

そこでPythonを使用して情報の整理を行ってみることにした。

  1. My clippings.txtをローカルに保存する
  2. Pythonで読み込んでcsvに変換する
  3. Excelで開き、必要に応じて書籍名でフィルタをかける

プログラム

プログラムとしては以下のような感じでよい。 途中、ハイライトした部分が複数行になるようなパターンがあったが、これは文字結合で回避できた。 また、空行はそのまま空行として出力させた。Excelであれば列ごと削除もできるので、この辺りは作りこまない。

# Pandasの読み込み
import pandas as pd

# 空のデータフレームを定義。タイトルと位置と空行とハイライト文言
cols = ["title", "position", "space", "highlights"]
df = pd.DataFrame(index=[], columns=cols)

# データ読み込み
text_data = open("/path/to/My Clippings.txt", "r")
lines = text_data.readlines()

# デリミタなどの必要情報を定義
delimiter = "=========="
flg = 0
highlights_str = ""
recode_base = ["","","",""]

# 繰り返し処理
for line in lines:
    
    # まずは末尾の改行コードを削除
    line = line.replace("\n", "")

    # デリミタに等しい場合
    if line == delimiter:
        
        # ハイライト文字列を設定し、レコードを作成。データフレームに追加
        recode_base[3] = highlights_str
        record = pd.Series(recode_base, index=df.columns)
        df = df.append(record, ignore_index=True)
        
        # 初期情報を再定義してcontinue
        flg = 0
        highlights_str = ""
        recode_base = ["","","",""]
        continue
    
    # ハイライト箇所は複数行にまたがることがあるので、
    # デリミタが出現するまではhighlights_strに文字結合しておく
    if flg >= 3:
        highlights_str += line
        flg += 1
        continue
    
    # タイトル、位置、空行についてはrecode_baseのそれぞれの位置に設定
    recode_base[flg] = line
    flg += 1

# CSVファイルで出力。
# encodeの設定はExcelで開いたときに文字化けするのを防ぐためutf_8_sigとした
df.to_csv("/path/to/export.csv", encoding='utf_8_sig')

text_data.close()

これで問題なく出力できた。目的達成!

IDLの構造体の要素には丸括弧でアクセスできる件

IDL(Interactive Data Language)でCSVを読み込んだ場合には、構造体として取得される。
このread_csvで読み込んだ結果には少々癖がある。

  • 読み込んだcsvの列数が1桁で収まる場合には、各列をFIELD1, FIELD2のようなプロパティでアクセスする
  • 読み込んだcsvの列数が2桁になる場合には、各列をFIELD01, FIELD02のようなゼロパディングしたプロパティでアクセスする

これがループ処理で一気にアクセスする際に厄介になるので、簡単なアクセス方法を検討してみると、構造体に丸括弧でアクセスできると記載されていた。

www.l3harrisgeospatial.com

以下、該当箇所を引用

Access Fields by Tag Number
A tag can be referenced using its index rather than its tag name. The tag index should be enclosed in parentheses, as follows:

    Variable_Name.(Tag_Index)... ... ...

The Tag_Index ranges from zero to the number of fields minus one.

CSVのサンプルとして、国勢調査の時系列データ「男女別人口-全国,都道府県(大正9年~平成27年)」を拾ってきて、ループ処理で簡単にアクセスできるか見てみる。

www.e-stat.go.jp

pro testAccessCSVByTagNum
  COMPILE_OPT idl2
 
  path = WGET("https://www.e-stat.go.jp/stat-search/file-download?statInfId=000031524010&fileKind=1", $
    FILENAME="c01.csv")
  csv_data = READ_CSV(path)
 
  ntag = N_TAGS(csv_data)
  csv_data_list = list()
  for index = 0L, ntag-1 do begin
    csv_data_list.Add, csv_data.(index)
  endfor
  help, csv_data_list
  help, csv_data_list[0]

  ; output
  ; CSV_DATA_LIST   LIST  <ID=29  NELEMENTS=9>
  ; <Expression>    STRING    = Array[982]

  ; Convert list to array
  csv_data_arr = csv_data_list.ToArray()
  help, csv_data_arr 

  ; output
  ; CSV_DATA_ARR    STRING    = Array[9, 982]  

end

意外に便利だったので備忘録として残しておく。

WSL2のGUI環境を構築した話と、その時にMcAfee Endpoint Securityの設定でupdateでこけた話

在宅ワークが長期化したことで、ちょっと使えるmacLinuxが手元にないのがしんどくなってきた。

社内ネットワーク上には、一応、検証機としてmacLinuxがある。
ただ、都度VPNで会社のネットワークに接続し、tera termでアクセスして使用するというを何回もやると、意外に面倒。

本日少しまとまった時間が取れたので、Windows Subsystem For Linuxをインストールし、いちいちVPNで社内ネットワークにつながなくても使えるLinuxの環境を構築してみた。

このときの備忘録を残しておく。使用しているOSはWindows 10 Pro バージョン 1909 (ビルド: 18363.1139)。

  • 1. WSL2を使える設定にしておく
  • 2. WSLをWindows Storeから落としてユーザ登録する
  • 3. Updateできない。
  • 4. McAfee Endpoint Securityのファイアウォール
  • 5. GUIの導入
  • 6. 接続確認
続きを読む

IDLの積演算で行われる型変換について勘違いしていた件

公私ともにIDLという言語を使用している。
これは、CやFortranに影響を受けたプログラミング言語で、さっと使えるため、ちょっとした計算や処理はこれで書くことが多い。

www.harrisgeospatial.co.jp

今回、これでプログラムを書いていた際におかしな現象を確認したので、備忘録として残しておく。

大容量のファイルを読み込みたい

ある画像データを考える。最近だと広い範囲をセンチメートルオーダの高解像度のドローンで取るようなことも珍しくないので、ファイル容量は近年増加傾向にある。
こういった、時には数十GBにも達するような大容量のファイルを一度に読み込んで変数に設定する場合、処理に大きな負荷をかけることになるので好ましくない。

data = read_tiff("/path/to/bigSizeTiffData.tif") ; メモリに負荷がかかる。。

このような場合にはREAD_BINARYなどの関数を使用して、特定の範囲だけにアクセスすることが推奨される。
この関数のキーワードではデータタイプや読み込み開始位置を指定することができるので、ファイルへの柔軟なアクセスが行え、大きなファイルの処理の際にはよく使われている。

www.l3harrisgeospatial.com

たとえば、ある3次元のデータを考える。

  • XYZのそれぞれの次元が2000個の分解能を持つ
  • ある点(Xi,Yi,Zi)の点にFLOAT型で定義された浮動小数の値が定義されている

この場合のファイルサイズをバイト単位で計算すると、2000*2000*2000*4(FLOATは4バイトのサイズを占める) = 32,000,000,000で32GBの容量になる。

これを一気に読み込むのは現実的でない。
もし処理したい範囲がXが100の地点から10ポイント、Yが100の地点から10ポイント、Zが100の地点から10ポイントの都合1000個分だけの範囲に収まるなら、以下のような書き方ができる。
(※実際にはどのような順序で形でデータが格納されているかまで考慮する必要があるが、ここではあまり厳密には触れないものとする)

pnt = ULONG64(100)*100*100*4

readData = READ_BINARY($
  "/path/to/bigSize3dData.data", $
  ENDIAN="big", $
  DATA_TYPE=4, $ ; floatを示す番号
  DATA_START= pnt, $ ; 読み込み開始地点
  DATA_DIMS=[10,10,10]) ; 指定の場所から読んで[10,10,10]で取得
  help, readData ; readDataが[10,10,10]の配列と確認できる

今回何があったか。

上記のような方法で読み込もうとするときに、pntの計算でおかしなことが起きた。
読み込み位置の起点となるxiyiziを指定して読み込み開始位置を計算する際に、以下のような関数を用意していたが、この戻り値がどうもおかしい。

function get_xyz_point, x, y, z
    compile_opt idl2

    xmax = 2000
    ymax = 2000
    zmax = 2000
    floatCoff = LONG64(4)

    pnt = 0
    pnt += z * xmax * ymax * floatCoff
    pnt += y * ymax * floatCoff
    pnt += x * floatCoff

    return, pnt

end

途中まではうまく動いていたように思ったのだが、以下のように設定した際の戻り値が奇妙になっていた。

pnt = get_xyz_point(500, 500, 800)
print, pnt 
; answer > -4,379,869,184 であり、意図したものでない。。

デバッグで気づいたのだが、以下の計算結果がおかしくなっていた。

pnt += z * xmax * ymax * floatCoff 

どういった理由かというと、floatCoffでLONG64型を指定しているのでまず全てがLONG64型にスケールされて計算されるかと思っていたのだが、違ったらしい。
正しくは、先頭から順番に積を取っていき、その都度一番大きな型にスケールされるというのがIDLでの動作だったようだ。

IDL> help, z
Z               INT       =      800  ; 引数のzはint型
IDL> help, xmax
XMAX            LONG      =         2000 ; 内部で定義したxmaxはlong型
IDL> help, ymax
YMAX            LONG      =         2000 ; 内部で定義したymaxはlong型

IDL> help, z * xmax
<Expression>    LONG      =      1600000 
; z* xmaxまでは int * longなのでlong型

IDL> help, z * xmax * ymax
<Expression>    LONG      =  -1094967296 
; ここで int * long * long で long型となってしまう。
; 意図した 800*2000*2000 = 3,200,000,000 はlongの範囲外

IDL> help, z * xmax * ymax * floatCoff
<Expression>    LONG64    =            -4379869184 
; 上記のlongの結果はすでにマイナス。
; このもとで long * long64 でlong64型にスケールされたが
; 前段階の z * xmax * ymax の時点で既に意図した結果にはなっていない。。

内部で定義する値についても明示的にLONG64で定義すればこの問題は避けることができた。
大容量のファイルを扱う場合には、常に型を意識しないと怖い目にあうと肝に銘じておく。

最終的な修正プログラムは以下の通り。

function get_xyz_point, x, y, z
    compile_opt idl2

    xmax = LONG64(2000)
    ymax = LONG64(2000)
    zmax = LONG64(2000)
    floatCoff = LONG64(4)

    pnt = 0
    pnt += z * xmax * ymax * floatCoff
    pnt += y * ymax * floatCoff
    pnt += x * floatCoff

    return, pnt

end

pnt = get_xyz_point(500, 500, 800)
print, pnt ; answer > 12,804,002,000 という意図したものになっている。

黙読が記憶に与える影響についての論文を読んでみた

はじめに

現在読書猿さんの著書である『独学大全』を読んでいる。

この本の中で黙読について扱っている箇所があり、以下のような点について触れられていた。

  • 声に出す(心の中を含めた:内声化された)読書は、読む速度を遅くする
  • 子供は音読で本を読んだ方が理解も記憶もできるが、大人は違う
  • 大人は黙読の方が速度だけでなく、理解度や記憶においても優れた結果を得られる

この根拠として以下の論文が著書の中では参照されており、Webで検索するとすぐに見つけることができた。 これを今回は読んでみたいと思う。

  • 音読と黙読が文書理解に及ぼす効果の比較
  • 長文の音読と黙読が記憶に及ぼす効果

ir.lib.hiroshima-u.ac.jp

repository.center.wakayama-u.ac.jp

上記の論文をみた限りの要点

専門外の分野なので所々不明なものはあるが、大まかな要点は以下の通りと受け取った。

  • 記憶できていたか、という点では黙読をおこなった方が成績がよかった
  • 速く読めたか、という点では黙読をおこなった方が成績がよかった
  • 理解できたか、という点では難しい文章であるほど黙読をおこなった方が成績がよかった

私の意見

理解力や記憶力においても黙読の方が効果的であったのは単純に驚きである。 これに対して何らかの理由付けを行おうと思うと、両論文で参照されていた森敏昭氏の論文で言及されていたものがあった。

ci.nii.ac.jp

この森氏の論文では黙読が文章の内容を体制化して記憶する場合に音読よりも有効であることが触れられており、この体制的な理解が複雑な文章への理解や記憶につながったのではと個人的には納得している。

また、今回の2つの論文で共通して特に私が重要と感じた点は、速く読めたのは黙読のほうだったという点にある。

私は一時期、速読の訓練を受けていたことがあり、この結果については概ね納得できる印象だった。

訓練の中で、文章を頭の中で声としてはっきり読んでいるうちは読書速度が上がることはなかった。 しかしながら、徐々にスピードを上げていって、頭の中で何か鳴っているがこれを声として認識できないような速度に達した時点が速読のブレークスルーになったと振り返って現在は感じている。

独学大全の中では意識して音を出さない、あるいは、別の言葉(「れろれろ」といった音)を内声化させ続けることで、意識的にこの状態に持っていく工夫が挙げられている。 これは、私の体験した何かが鳴っている状態まで意識的に持っていくための工夫の一つであると考えれば、速く読めるようになるのは納得である。

ブログはじめました

はじめまして。めたへいと申します。
GIS(地理情報システム: Geographic Information System)という分野を専門にしているアプリケーションエンジニアです。

日々学んだことをアウトプットする先として、ブログを始めました。
定期的に更新していく予定です。

宜しくお願い致します。