pythonでヒストグラムを正規化する

pythonヒストグラムを正規化(面積を1.0にする)する方法です

サンプルコード

import numpy as np
import matlplotlib.plt as plt


data=[1,2,2,3,3,3,4,4,4,4]

# 正規化されたヒストグラムを計算する
hist, edges = np.histogram(data, density=True)
w = edges[1] - edges[0]
hist = hist * w

# グラフにプロット
plt.bar(edges[:-1], hist, w)

# 正規化されていることを確認
print( sum(hist) )

注意点

normed=True は使わない

オプション normed は挙動が紛らわしいという欠点があったため廃止となりました.

ネット上には normed=True を使う例が散見されますが,それは過去の話です.無視しましょう.

density=True では正規化されない

ネット上には normed の変わりに density を使えという記載もありますが,これも間違いです.

np.histgram() や plt.hist() で density=True を指定してもヒストグラムは正規化されません*1

たとえば以下のコードを実行しても sum(hist) は1.0 にはなりません

data=[1,2,2,3,3,3,4,4,4,4]
hist, edges = np.histogram(data, density=True)
print( sum(hist) )
# 結果は3.3が表示される.つまりhistは 正規化されてない

sum(hist)が1.0になるようにするには

# ヒストグラムを計算,正規化する
hist, edges = np.histogram(data, density=True)
w = edges[1] - edges[0]
hist = hist * w

print( sum(hist) )

と言った感じのコードが必要になります

*1:厳密には,たまたま w=1 となるようなデータの場合は density=True を指定するだけで正規化されます

macOS に TexLive 2020 をインストールする

macOSTeX環境を texlive2020 に更新したので手順をまとめます.

この記事は以下のエントリを macOSTex Live 2020 に合わせて更新・加筆したものです.

BasicTeX

パッケージはMacTeXのサブセット版である BasicTeX を使いました.

MacTeXは全パッケージをインストールすると 6GB程度のディスク容量を消費します (Tex live 2020の場合).

一方サブセット版である BasicTeX は最低限のパッケージしかインストールしません.必要な容量は 277MB程度です.(TexLive 2020の場合)

不足パッケージは後から簡単にインストールできるので実用上の問題もありません

ダウンロード&インストール

https://tug.org/mactex/morepackages.html から mactex-basictex-20200407.pkg をダウンロードします

サイズはたったの83MBですのでダウンロードはすぐに終わるはずです.

これをインストールします

なおMacTeXは

  • 2020 通常版 /usr/local/texlive/2020
  • 2020 BasicTeX /usr/local/texlive/2020basic
  • 2019 BasicTeX /usr/local/texlive/2019basic

という感じでディレクトリを分けてファイルを配置するので,異なるバージョンのTeX環境を混在できます.


デフォルトのTeX環境の選択は,"TeX Distribution"というアプリで行います.
(メニューの”システム環境設定"にアイコンがあります.Spotlight検索で TeX Distribution でも起動できます)
このアプリは

  • /Library/TeX/texbin
  • /Library/TeX/Root
  • /Library/TeX/Local
  • /Library/TeX/Documentation

というシンボリックリンクを管理していて,これらリンクでデフォルトのtex環境を選択する仕組みになっています.

たとえば mactex-basic.pkg をインストールすると,/Library/TeX/texbin のリンク先は

$ readlink -f  /Library/TeX/texbin
/usr/local/texlive/2020basic/bin/x86_64-darwin

という感じで,/usr/local/texlive/2020basic/bin/x86_64-darwin になります

初期設定

basictex をインストールしたら,必要なパッケージを追加インストールします

方法は二つ

  • Tex Live ユーティリティーというGUIを使う方法
  • CUIでtlmgrコマンドを使う方法

あります.

ここでは,説明が簡単,という理由でCUIを使います.

まずターミナルを起動します.そして以下のコマンドを実行します.

$ sudo tlmgr update --self  --all

オプションの意味はそれぞれ次のようになります.

  • --self tlmgr 自身を更新する
  • --all インストール済みパッケージを更新する


最後に,個人的に良く使うパッケージを追加インストールします.

$ sudo tlmgr install  type1cm subfigure dvipdfmx multirow xstring logreq biblatex latexmk  biber pxjahyper

日本語環境の設定

日本語の設定を行います.

用紙サイズの設定

デフォルトをA4サイズにします

$ sudo tlmgr paper a4
日本語用パッケージのインストール

メタパッケージ collection-langjapanese をインストールすると,日本語関連のパッケージが一括でインストールできます.しかし collection-langjapaneseをインストールすると日本語フォントが大量にインストールされ,フォントだけで700MBほどディスクを消費します

macOSヒラギノフォントを利用する場合,この700MBは無駄になります.

そこで collection-langjapanese はインストールせずに,必要なパッケージだけをインストールする方針でセットアップを行います.

日本語を扱う際,最低限必要なパッケージは

でしょう.今回はとりあえずこれだけ入れて,後のパッケージは必要になった時に追加インストールすることにします

$ sudo tlmgr  install ptex platex jsclasses

次に,日本語フォントのセットアップを行います

TLContirb リポジトリの追加登録

ヒラギノフォントなどのmacOS 同梱のフォントを使うための設定です

ライセンスの関係でmacOS 同梱のフォントを使うためのパッケージ達は別リポジトリ( TLContrib )へ分離されています.

そこでまずはこのリポジトリを登録します.

$ sudo tlmgr repository add http://contrib.texlive.info/current tlcontrib
$ sudo  tlmgr pinning add tlcontrib '*'

日本語フォント周りで重要なパッケージは

  • cjk-gs-integrate-macos (cjk-gs-integrateのmacOS版,フォントの設定を行うスクリプトです)
  • japanese-otf-nonfree japanese-otf-uptex-nonfree ptex-fontmaps-macos
  • cjk-gs-integrate adobemapping

です

まとめてインストールします

$ sudo tlmgr  install cjk-gs-integrate-macos  japanese-otf-nonfree japanese-otf-uptex-nonfree ptex-fontmaps-macos  cjk-gs-integrate adobemapping
フォントの登録

cjk-gs-integrate-macos というスクリプト(cjk-gs-integrate のmacOS版)がインストールされているはずです.

これを使って macOS のフォントを登録します

$ sudo cjk-gs-integrate --link-texmf --cleanup
$ sudo cjk-gs-integrate-macos --link-texmf --force
$ sudo mktexlsr
登録ずみフォントの確認

フォントの設定用に kanji-config-updmap-sys というスクリプトがインストールされます.

このスクリプトで現在の設定を確認します.

$ sudo kanji-config-updmap-sys status

以下の出力が得られます

CURRENT family for ja: haranoaji (variant: -04)
Standby family : haranoaji
Standby family : hiragino-highsierra
Standby family : hiragino-highsierra-pron
Standby family : kozuka
Standby family : ms
Standby family : toppanbunkyu-highsierra
Standby family : yu-win10
フォントの変更

pdfに埋め込むフォントを指定します

ヒラギノを指定します

$ sudo kanji-config-updmap-sys --jis2004 hiragino-highsierra-pron

texlive 2020 で導入された「原ノ味フォント」を指定する場合は次のようにします.

$ sudo kanji-config-updmap-sys --jis2004 haranoaji

その他こまごまとした設定

自動で extractbb が実行されるようにする

以下の内容で /usr/local/texlive/texmf-local/web2c/texmf.cnf を用意します

shell_escape_commands = \
bibtex,bibtex8,bibtexu,upbibtex,biber,\
kpsewhich,\
makeindex,mendex,texindy,xindy,\
mpost,upmpost,\
repstopdf,epspdf,extractbb

古い TeX環境の削除

uninstallは簡単です

MacTex 2019 を消したい場合は /usr/local/texlive/2019basic をディレクトリ毎削除するだけです

$ sudo rm -rf /usr/local/texlive/2019basic

トラブルシューティング

pdfに画像が貼れない/gsコマンドが無い
ghostscript (gsコマンド) がインストールされていないと dvipdfmx 経由で作成する pdf に画像が貼れない場合があります.ghostscript をインストールするには https://tug.org/mactex/morepackages.html の mactex-additions.pkg から ghostscriptを選択してインストールするのが楽そうです.
LaTeX It を使いたい
gsコマンドが必要なので,あらかじめ上記の手順でghostscriptをインストールして http://www.chachatelier.fr/latexit/ からdmgをダウンロード,インストールする.
TeX Live Utility.app を使いたい
https://code.google.com/p/mactlmgr/
dvipdfmx がCould not find encoding file “H”. というエラーを出す
adobemapping パッケージをインストールすると直ります
日本語フォントが使えなくなった
Tex live 2018 でフォント周り(パッケージ構成やコマンド名)に少し変更が入っているようです.上記手順を参考にフォント周りの設定を見直してください

まとめ

インストール後の /usr/local/texlive/2020basicのサイズは 585MBでした

参考までに

  • /usr/local/texlive/2020basicのサイズは 585MB
  • /usr/local/texlive/2019basicのサイズは 586MB
  • /usr/local/texlive/2018basicのサイズは 546MB

Tex live 2020は全部インストールすると 6GB程度なので,かなりディスク容量を節約できたようです.



[改訂第7版]LaTeX2ε美文書作成入門
奥村 晴彦 黒木 裕介
技術評論社
売り上げランキング: 4,008


学生・研究者・技術者のためのLATEXを用いた論文作成術
渡辺 徹
プレアデス出版
売り上げランキング: 233,232

pythonのヒアドキュメントと変数展開

python のヒアドキュメントの使い方をメモします

ついでにpython-3.6で導入された f-strings を使って変数置換も行います

x=123
y="ABC"

tmp = f'''
x is {x}.
y is {y}.
x is {x}.
'''[1:-1]

print(tmp)

ポイント

  • f"..." と文字列の先頭に "f"を付けると変数置換を行います(これを f-strings と呼びます)
  • 変数置換のプレースホルダは"{}"で指定します
  • 上記コードの "[1:-1]" は,文字列の先頭の改行と末尾の改行を取り除くためのものです


f-strings は python-3.6 で導入された機能です.従来の文字列の変数置換は format関数を使っていました

x=123
"x is {foo}".format(foo=x)

f-stringsを使うと

x=123
f"x is {x}"

となります


上記コードの "[1:-1]" は,文字列の先頭の改行と末尾の改行を取り除くためのものです

Linuxでディスプレイ解像度を自動切替する方法

Linuxでディスプレイ解像度を自動で切り替える方法です.

  • ノートPCで外部モニタを使う場合
  • ノートPCでサスペンドから復帰後,解像度がおかしくなる場合
  • 画面切替機(キーボードスイッチャ,パソコン切替器)を使っている場合

などに,毎回解像度を手動で設定し直す必要がなくなります.便利です

設定方法

autorandr を使います.

インストールはaptが使える環境なら以下の通りです

$ sudo apt install autorandr

ソースコードGitHub で公開されています.
github.com
ubuntucentos などでもパッケージが標準で用意されているそうです.

使い方

  1. ディスプレイの設定を行います
  2. 設定が終わったら,設定をファイルに保存します.
  3. あとはホットプラグです
    • ディプレイを接続するなどしたら,そのタイミングで自動的に設定が切り替わります.

便利ですね

2.の設定保存の操作では,以下のコマンドを使います.

$ autorandr --save ”名前”

名前はそのまま設定ファイルの名前として流用されるので,半角英数が良いでしょう.

例えばデフォルト設定を保存する場合は

$ autorandr --save default

などとします.

保存した設定の一覧は以下のコマンドで確認できます

$ autorandr

autorandr の動作原理

autorandr には大きく二つの機能があります

画面設定の save と load

ディスプレイ設定を保存するには以下のコマンドを実行します

$ autorandr --save 名前

このコマンドを実行すると ~/.config/autorandr/名前/ と言うディレクトリが生成され,以下のファイルが生成されます

  • ~/.config/autorandr/名前/config
  • ~/.config/autorandr/名前/setup

~/.config/autorandr/名前/config には解像度などのディスプレイ設定が保存されています.X window system では,画面解像度などのディスプレイに関する設定やその変更方法は, RandR extension と言う仕様で管理されています.autorandr は RandR extensionを使って,解像度の設定などを調べて,その情報をファイルに保存します.

~/.config/autorandr/名前/setup にはハードウェア情報(接続されたディスプレイのEDIDなど)が保存されています

保存した設定の一覧は以下のコマンドで確認できます

$ autorandr

保存した設定を読み込んで,ディスプレイ設定を変更するには以下のコマンドを実行します

$ autorandr --load 名前
ホットプラグ

autorandr はホットプラグに対応しています.ディスプレイをつないだり,サスペンドから復帰すると,autorandr が自動で起動されます.

これは udev を使って実現されています.

ディスプレイが検出されると Linuxカーネルdrm サブシステムが反応します.そこで udev を使ってdrmサブシステムに変化があった場合は autorandr を実行します.

具体的には,debian の場合だと /lib/udev/rules.d/40-monitor-hotplug.rules が udev のルールになっています.

ACTION=="change", SUBSYSTEM=="drm", RUN+="/bin/systemctl start --no-block autorandr.service"

このルールでまず udev 経由で systemctl にジョブをキューイングします

autorandr.service は /lib/systemd/system/autorandr.service で定義されています

[Unit]
Description=autorandr execution hook
After=sleep.target
# Note: StartLimitInterval was renamed to StartLimitIntervalSec in systemd-230.
# See autorandr bug #69. Do not rename for now, as the old name is kept for
# compatibility.
StartLimitInterval=5
StartLimitBurst=1

[Service]
ExecStart=/usr/bin/autorandr --batch --change --default default
Type=oneshot
RemainAfterExit=false

[Install]
WantedBy=sleep.target

つまりディスプレイが接続されると,

/usr/bin/autorandr --batch --change --default default 

が一度だけ実行されます.

この際の /usr/bin/autorandr の挙動は以下の通りです.

  • 検出したディスプレイに対応する randr の設定があればそれを load
    • 細かく言うと,まず接続されたディスプレイのEDIDなどと ~/.config/autorandr/*/setup に保存された情報を照らし合わせて,対応するものが見つかれば その config をloadします.
  • 新しいディスプレイの場合は "default" をload
    • 細かく言うと,上記処理で対応する autorandr/*/setupが見つからない場合,となります.
    • "--default default"の設定で defaultを指定しているので, ~/.config/autorandr/default/config をloadします

と言うわけで,デフォルト設定が必要な場合は,設定を

$ autorandr --save default 

で保存しておくと良いかもしれません.

Pythonで文字列をDatetime型に変換する方法

Pythonで文字列を時刻に変換する方法です

時刻を表す文字列

時刻を表す文字列にはいくつかのフォーマット(書式)があります

ISO8601の例

  • 2020-06-14T13:04:46+09:00

RFC 3339の例

  • 2020-06-14T04:04:46.000Z
  • 2020-06-14T04:04:46.000000Z

これらは全部同じ時刻を表しています.

末尾の"Z"や"+09:00"はタイムゾーンを表しています.
Zは“Zulu time”で +00:00,UTCと同じ意味です.+09:00は日本のタイムゾーン(JST)です.

文字列からDatetimeへの変換

ISO8601とRFC3339の書式は似ているようで,微妙に違いがあります.
両者を混在させて pythonで扱う場合には注意が必要です.

ISO8601の場合

Python 3.7以降なら datetime.datetime.fromisoformat() を使うだけで変換できます

import datetime
s = "2020-06-14T13:04:46+09:00"
print ( datetime.datetime.fromisoformat(s) )
RFC3339 の場合

文字列の末尾が"Z"の場合 datetime.datetime.fromisoformat() は, ValueErrorの例外を出します.

ValueErrorの例外の例

ValueError: Invalid isoformat string: '2020-06-14T13:04:46+09:00Z'

この場合は "Z"を無視すれば Datetimeに変換できます

import datetime
s = "2020-06-14T04:04:46.000Z"
print ( datetime.datetime.fromisoformat(s[:-1]) )

重要な点が2つあります

1点目. "Z"は +00:00 なので,

  • "2020-06-14T04:04:46.000Z"
  • "2020-06-14T04:04:46.000"
  • "2020-06-14T04:04:46.000+00:00"

は全部同じ時刻になります

2点目.

  • 2020-06-14T13:04:46+09:00
  • 2020-06-14T04:04:46.000Z

は同じ時刻です

タイムゾーンを補正しようとして
"2020-06-14T04:04:46.000+00:00"に9時間を足してしまうと
"2020-06-14T13:04:46.000+00:00"になります
これはJSTでは "2020-06-14T22:04:46.000+09:00"になります.つまり間違いです.

このように間違って+09:00を加算してしまわないようにするのがポイントになります

RFC3339形式を ISO8601形式に変換する

RFC3339形式の文字列を,ISO8601形式に変換してみます

タイムゾーンJSTに変更します

import datetime
import tz

s = "2020-06-14T04:04:46.000Z"
t =  datetime.datetime.fromisoformat(s[:-1]) 

# タイムゾーン JSTの情報を取得
JST = tz.gettz('JST')

# datetimeオブジェクト t のタイムゾーンを変更
t = t.astimezone(JST)

print ( t.isoformat() )

これで ISO8601形式で '2020-06-14T13:04:46+09:00' が表示されます

Pythonでコマンドラインツール(CUIとかCLI)を作るときのTips

PythonCUIとかCLIを作る際のTips です.

下記の内容を順に書いていきます

  • エラー表示を見やすくする
    • 使い方の説明
    • 実際のコード例
  • コマンドライン引数の処理
    • 使い方の説明
    • 実際のコード例

Tips は今後も追記していく予定です(最終更新: 2020-06-13)

エラー表示を見やすくする(使い方の説明)

coloredlogs モジュールを使うと、エラーメッセージをカラフルに見やすく表示できます

import coloredlogs, logging
coloredlogs.install(fmt="%(message)s", level=logging.DEBUG)

logging.error("Error")
logging.warning("No such file")
logging.debug("parameter is %d" % (123))

メッセージにはレベルが定義されています

レベル メソッド レベルの数値
logging.CRITICAL logging.critical() 50
logging.ERROR logging.error() 40
logging.WARNING logging.warning() 30
logging.INFO logging.info() 20
logging.DEBUG loggin.debug() 10

そして setLevel() でレベルを指定すると,その値より小さなレベルのメッセージは画面には表示されなくなります.

たとえば

logging.getLogger().setLevel(logging.WARNING)

logging.error("Error")
logging.warning("No such file")
logging.debug("parameter is %d" % (123))

を実行すると logging.debug()の部分は画面には表示されません

このようにデバッグ情報の表示のOn/Offなどを柔軟に切り替えることができます

エラー表示を見やすくする(実際のコード例)

coloredlogs は標準ではインストールされません.coloredlogsがインストールされてない場合はインストール方法を画面に表示するようにしておくと便利です

#!/usr/bin/env python3

import sys
try:
    import coloredlogs, logging
except ImportError as err:
    print("\nPlease install %s. You may install it by using:" % err.name)
    print(" pip3 install %s\n" % err.name)
    sys.exit("Abort.")

coloredlogs.install(fmt="%(message)s", level=logging.DEBUG)

モジュールがインストールされてない場合,ImportErrorの例外が発生します. そこで try: except で例外処理を実装します

ImportError例外は,例外を引数errで受けとると, err.name の値で例外を起こしたモジュール名が取得できます.


コマンドライン引数の処理(使い方の説明)

python標準モジュール argparse をつかいます

import argparse
parser = argparse.ArgumentParser()

# コマンドラインオプションを追加
parser.add_argument('filename', type=str, nargs='?')

# コマンドライン引数を処理
args = parser.parse_args()

# コマンドライン引数は args のメンバ変数になります
print(args.filename)

parse_add_argument の使い方

add_argumentの使い方:コマンドライン引数の追加
parser.add_argument('filename', type=str, nargs='?')

args = parser.parse_args()

print(args.filename)
add_argumentの使い方:引数不要のオプションの追加

action='store_true' を指定します

parser.add_argument('--list', action='store_true', help='show a list of dataset')
args = parser.parse_args()

print(args.list)
add_argumentの使い方:引数必須のオプションの追加
parser.add_argument('--value')
args = parser.parse_args()

print(args.value)
add_argumentの使い方:複数回指定できる引数なしオプション
parser.add_argument('-v', '--verbose', action='count', help='Increase verbosity level.')
args = parser.parse_args()

print(args.verbose)

動作は

  • コマンドラインで -v がN回指定されていれば args.verboseの値は N
  • "-v" が一回も指定されていなければ args.verbose の値は None

になります


コマンドライン引数の処理(実際のコード例)

コマンドラインオプション "-v" で,デバッグメッセージの表示をOn/Offするサンプルです

コマンドライン引数で,以下の動作をします

$ ./sample.py   --help    # ヘルプメッセージを表示
$ ./sample.py         #  warning のメッセージまで表示
$ ./sample.py -v     #  info のメッセージまで表示
$ ./sample.py -vv   #  debug のメッセージまで表示

sample.pyの中身

#!/usr/bin/env python3

import sys
import argparse

try:
    import coloredlogs, logging
except ImportError as err:
    print("\nPlease install %s. You may install it by using:\n" % (err.name))
    print(" pip3 install %s\n" % (err.name))
    sys.exit("Abort.")
else:
    coloredlogs.install(fmt="%(message)s", level=logging.DEBUG)


parser = argparse.ArgumentParser()
parser.add_argument('-v', '--verbose', action='count', help='Increase verbosity level.')

args = parser.parse_args()

if args.verbose is None:
    logging.getLogger().setLevel(logging.WARNING)
else:
    table = [logging.WARNING, logging.INFO, logging.DEBUG]
    level = min(len(table)-1, args.verbose)
    logging.getLogger().setLevel(table[level])

logging.critical("critical message")
logging.error("error message")
logging.warning("warning message")
logging.info("info message")
logging.debug("debug message")

Python で正規表現を使うときのエラー対策

正規表現を使うときは,正規表現のエラーチェックをきちんと実装しておいたほうが安全です

例外を使って実装すると次のようになります

import re

pattern = "^c++"
prog = None
try:
    prog = re.compile(pattern)
except re.error as msg:
    print("error in regex /%s/; %s" % (pattern, str(msg)))

行頭の c++ にマッチさせたいので正規表現として ^c++ と書いてありますが,これは正しくありません.

上記サンプルコードを実行すると,例外が発生して,エラーメッセージが表示されます.

error in regex /^c++/: multiple repeat at position 3

"multiple repeat"とあるように ++ が連続しているのは正規表現として正しくありません

上記の例だと c++ にマッチさせたいので ^c\+\+ と訂正する必要があります