カンテラの光の下で

dNaga392's memorandom

Excelのアドレス文字をPythonで組み立てた

概要

  • xlwings のAPIが遅いので、文字列処理でアドレス文字を求めました

動機

皆さんご存知の通り、Excelにはセルのアドレスを表すA1 とか C3 ってありますよね。

これってVBAだと、RangeオブジェクトのAddressプロパティで取得できて、 xlwingsでもxlwings.Range クラスの get_address メソッドで取得できるんです。

ただ、この処理がちょっと遅い。

一回の処理に0.02~0.04秒程度かかるので、頻繁に呼び出すと急激に処理時間が増えていきます。

そこで、多少のリスクはあってもより短い時間で取得できないかと文字列処理を試みました。

コード

作成したコードは次のとおりです:

列名は、A, B, ..., Z, AA, AB, .... というように記述されるため、26進数でも27進数でもありません(A0などはない)。 そのため、桁上げのとこには少し工夫を入れてあります。

速度的には、上述の処理でほぼゼロ秒まで抑えられました(計測時間では、小数点15桁目まで表示しても0が続くほど)。

感想

コードのコメントにもある通り、xlwings でも得られます。 ではこのコードは同等の処理かというと、サポートしていない機能もあります。

  • 引数チェックがない
    • 不正な値が渡されても処理が進む
  • 最大列数/行数を超えた場合も、アドレス文字列は得られる
    • xlwings だと、上限を超えた場合は例外が送出される

とはいえ、この処理の後にxlwings.Rangeクラスを使うならば、そこでエラーが出るので大きな問題はないかなと思ってます。

スマートではないかなと思いつつも、パフォーマンスが向上できたので個人的には満足です!

参考資料

Pythonのデフォルト引数で注意すること

概要

  • 引数のデフォルト値は 1 度だけしか評価されない
  • つまり、デフォルト引数で指定した値は、関数実行ごとには初期化されない
  • (list や dict のような)変更可能なオブジェクトを指定したい場合は、代わりに None を指定するとよい

現象

関数のデフォルト引数値に、list や dict のように、変更可能(ミュータブル)なオブジェクトを指定したとき、 これは1度しか評価されず、2回目以降はそれを繰り返し使用する。

def f(a, L=[]):
    L.append(a)
    return L

print(f(1))  # => [1]
print(f(2))  # => [1, 2]
print(f(3))  # => [1, 2, 3]

この現象は、 Python ドキュメントに 重要な警告 として記載されている。

これは奇妙な現象にも思えるが、オブジェクトに対して変数を割り当てるという Python の視点から解釈すれば納得することもできるだろう (とはいえ、期待する動作ではないのだが。。。)。

対策

一般的に、このような場合は変更可能(ミュータブル)なオブジェクトの代わりに None を使用する。

def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L

print(f(1))  # => [1]
print(f(2))  # => [2]
print(f(3))  # => [3]

参考資料

【markdown-it】markdownをパースするHTMLを作ってみた

要点

動機

これまで、 markdown ファイルは markdown viewer (Firefox Add-on)を使って表示していたのだが、 他人に見せるときには必ずしもそのアドオンがあるわけでなく、生のmarkdownが表示されることがあった。

そこで、markdownを表示するHTMLを作ってみることにした。

方法

方法は次の手順に分けて対応した。

  1. markdown を読み込む
  2. markdown をパースする

1. markdown を読み込む

markdown の読み込みには、Fetch API を使った。

developer.mozilla.org

Web素人なため、やりたいことがうまく説明できずたどり着くまでに時間がかかったが、 Fetch API により、同じフォルダにある markdown を読むことができた。

fetch('./index.md')
  .then(response => {
    return response.text();
  })
  .then(body => {
     document.body.innerHTML = body;
  });

2. markdown をパースする

markdown のパースには、marked と markdown-it の2つのライブラリが考えられた。

marked を先に知っていたので、まずはこちらでと考えていたが、 markdown-it が拡張性に富んでいるの点と、実際に使っていてオススメだという周囲の助言があったので、markdown-it を採用した。

fetch('./index.md')
  .then(response => {
    return response.text();
  })
  .then(body => {
     var md = window.markdownit();
     document.body.innerHTML = md.render(body);
  });

ライブラリは、環境構築を極力なくすため、 cdnjs から利用することにした。

<script src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/8.4.1/markdown-it.min.js"></script>

これでもそこそこパースできたが、タイトルがファイル名となってしまいかっこ悪かった。

動機の原点である markdown viewer では、最初のヘッダーがタイトルになっていたので、 これを実現することにした。

実現には、作者 Keith L Robertson 氏のリポジトリを参考とした。

github.com

結果

f:id:dNaga392:20180320011038p:plain

簡単な markdown を HTML に表示することができた。

拙い出来ではあるが、githubに公開した(https://github.com/dNaga392/md-index)。 興味があれば、参考にしたりマサカリ投げていただきたい。

参考

【matplotlib】散布図を3D描画する

要点

使用例

import matplotlib.pyplot as plt
import seaborn as sns
from mpl_toolkits.mplot3d import axes3d  # Axes3D のために必要

df = sns.load_dataset('iris')
xs = df['sepal_length']
ys = df['sepal_width']
zs = df['petal_length']

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection='3d')

ax.scatter(xs, ys, zs)

ax.set_title('Matplot 3d scatter plot')  # タイトル
ax.set_xlabel('sepal_length')  # X軸ラベル
ax.set_ylabel('sepal_width')  # Y軸ラベル
ax.set_zlabel('petal_length')  # Z軸ラベル

plt.show()

mpl-ax3d-scatter.png

特定のグループごとに描画する場合

hue = 'species'
labels = set(df[hue])
dataset = []
for x in labels:
    xs = df[df[hue]==x]['sepal_length']
    ys = df[df[hue]==x]['sepal_width']
    zs = df[df[hue]==x]['petal_length']
    dataset.append((xs, ys, zs))

fig = plt.figure()

ax = fig.add_subplot(1, 1, 1, projection='3d')
for data, label in zip(dataset, labels):
    ax.scatter(xs, ys, zs, label=label)
ax.set_title('Matplot 3d scatter plot')  # タイトル
ax.set_xlabel('sepal_length')  # X軸ラベル
ax.set_ylabel('sepal_width')  # Y軸ラベル
ax.set_zlabel('petal_length')  # Z軸ラベル
ax.legend(loc=2, title='legend', shadow=True)  # 凡例

plt.show()

mpl-ax3d-scatter-hue.png

【matplotlib】散布図を描画する

要点

使用例

import matplotlib.pyplot as plt
import seaborn as sns

df = sns.load_dataset('iris')
x = df['sepal_length']
y = df['sepal_width']

fig = plt.figure()

ax = fig.add_subplot(1, 1, 1)

ax.scatter(x, y)
ax.set_title('Matplot scatter plot')  # タイトル
ax.set_xlabel('sepal_length')  # X軸ラベル
ax.set_ylabel('sepal_width')  # Y軸ラベル

plt.show()

mpl-ax-scatter.png

特定のグループごとに描画する場合

fig = plt.figure()

ax = fig.add_subplot(1, 1, 1)

hue = 'species'
labels = set(df[hue])
dataset = [(df[df[hue]==x]['sepal_length'], df[df[hue]==x]['sepal_width']) for x in labels]
for data, label in zip(dataset, labels):
    x, y = data
    ax.scatter(x, y, label=label)
ax.set_title('Matplot scatter plot')  # タイトル
ax.set_xlabel('sepal_length')  # X軸ラベル
ax.set_ylabel('sepal_width')  # Y軸ラベル
ax.legend(loc='best', title='legend', shadow=True)  # 凡例

plt.show()

mpl-ax-scatter-hue.png

実を言うとTab Groupsはもうだめです。

f:id:dNaga392:20170130224504p:plain

今朝 Firefox を開くと、Tab Groups からの last notify のページが開きました。

記事によると、これまでアドオンとして提供されていたTab Groupsの開発が中止されるとのことでした。

タブグループ機能がFirefox本体から廃止され、その代わりになったのがこのTab Groupsで、 これまではQuicksaver氏が開発を続けてきました。

Quicksaver氏のTab Groupsは、軽快で安定した動作の高品質なアドオンで、 私のインターネットライフをより良いものとしてくれました。

しかしながら、Mozillaの示したFirefoxの今後の方針は、 アドオン開発の継続に大きな負荷をかけるものでした。

Quicksaver氏によると、いくらか抵抗し意見を交わしたものの Mozzilaの方針は変わることはなかったそうです。

そのため、開発を続けることに限界を感じたQuicksaver氏は Tab Groupsを始め、彼のアドオンの開発をすべて終えることにしたということです。

愛用していたTab Groupsの開発終了は大変残念ですが、 これをきっかけにタブの整理を進めてみるのもいいかもしれません。

これまで高品質なアドオンを提供してくれたQuicksaver氏に深い感謝を。


追記

forest.watch.impress.co.jp

そこで従来型アドオンは、順次“WebExtensions”ベースのアドオンへと置き換えられる。具体的には、4月に「Firefox 53」がリリースされるタイミングで“addons.mozilla.org(AMO)”における従来型アドオンの受け付けが終了となり、11月の「Firefox 57」以降は従来型アドオンがサポートされなくなる。

11月がリミットっぽい

【Git】 fatal: not under version control, source=A.txt, destination=B.txt

Gitで表題のようなエラーが出た。

ファイル名をチェックすると、ファイル名の一部が大文字小文字で異なっていた。 これは git を介さずにファイル名を変更したためとのこと[1]。

解決法には以下の2つがある。

  • 元のファイル名に戻す。
  • ファイル名変更をgitに伝える。

ここでは後者の方法を提示する。 手順は次の通りである。

  1. 元のファイル名でgit mv コマンドを実行する。
> ls
a.txt
> mv a.txt A.txt
> ls
A.txt
> git mv A.txt B.txt
fatal: not under version control, source=A.txt, destination=B.txt
> ls
A.txt
> git mv a.txt B.txt
> ls
B.txt

あるいは、ファイル名自体を変更する方法もあるが、手間なのでお勧めはしない。

> ls
a.txt
> mv a.txt A.txt
> ls
A.txt
>ls
A.txt
> mkdir dirB
> git mv *.txt dirB
fatal: not under version control, source=A.txt, destination=dirB/A.txt
> mv A.txt _A.txt
>ls
_A.txt
> git add .
> git commit -m "rename temp filename"
> mv _A.txt A.txt
>ls
A.txt
> git add .
> git commit -m "rename true filename"
> git mv *.txt dirB
> cd dirB
> ls
A.txt

参考資料

[1] rcmdnk.github.io