Λlisue's blog

つれづれなるままに更新されないブログ

Python で自己相関関数とかやる(途中・完成予定無し)

随分前に書こうと思って途中まで書いたけど面倒くさくなってやめたやつが下書きにあったからとりあえず公開。続きを書こうかと思ったけどびっくりするくらい覚えてないのでやめた。


どうも有末です。ご無沙汰しております。

研究関係でシグナルの自己相関関数やフーリエ変換について調べたので、未来の自分のためにメモを残そうと思います。 数学がそんなに得意ではない人間の解釈なのでご注意ください。訂正等のコメントは歓迎いたします。

前提知識というか数式

以下、特に断りがない限り入力として離散的な有限個数のデータを想定しています。

複素共役 (Complex conjugate)

今後式中に $\overline{f}$ の様な記載が出てきますが、これは次式で定義される複素共役 (Complex conjugate) を表しています。

$$
\overline{z} \equiv a - bi \qquad when ~~ z \equiv a + bi
$$

ただ、定義からもわかる通り虚数部がひっくり返っただけなので、実数しか扱わない実数値関数 (Real function) では $\overline{f} = f$ となります。 今回は実験データなどの実数値の解析を行うことを想定しているため定義上複素共役が出てきても、特に何かを行う必要はありません。

numpy では numpy.conjugate(x) として定義されています。

合成積 (Convolution)

今後式中に $f * g$ のような記載が出てきますが、これは次式で定義される合成積 (Convolution) を表しています。

$$
(f * g)(t) \equiv \int_{-\infty}^{\infty} f(\tau) g(t - \tau) d\tau
$$

定義上は連続関数である $f$ および $g$ を無限区間にて積分しますが、実験データなどは無限連続関数ではなく有限な離散値の集合になるため、集合 $a$ および $v$ の定義領域外(集合の領域外)を 0 と再定義し線形畳み込み (linear convolution)

$$
(a * v)(n) \equiv \sum_{m=-\infty}^{\infty} a_{m} v_{n - m}
$$

の計算を行います。なお、上式にて $m$ が集合 $a$ もしくは $v$ の定義域領域外に入った段階で積が 0 になり結果に影響を及ぼさないため事実上無限の計算は不要になります。

numpy では線形畳み込みを行う numpy.convolute(a, v) が定義されています。 この関数では渡された一次配列 a および v の値域がオーバラップする範囲 (N + M - 1 when N := len(a), M := len(v))の線形畳み込み結果を返します。

期待値 (Expected value)

今後式中に $E[x]$ のような記載が出てきますが、これは次式で定義される期待値 (Expected value) を表しています。

$$
\begin{align}
E[X] &\equiv \int_{-\infty}^{\infty} xf(x)dx \\
&\sim \sum_{i=1}^{\infty} x_{i} p_{i},
\end{align}
$$

上式において $X$ は $f(x)$ を確立密度関数として持つ確率分布、$x_i$ は離散的な各値で $p_i$ はその確率です。

numpy では numpy.mean(x) が定義されています。

共分散 (Covariance)

2種のランダムデータがどの程度シンクロしているかの指標として共分散(Covariance)が以下のように定義されます。

$$
cov(X, Y) \equiv E[(X - E[X])(Y - E[Y])]
$$

ここで $X$ および $Y$ は確率変数を表します。

numpy では numpy.cov(m, y) が定義されています。

相関という名前の数式

統計学と信号処理

In probability and statistics, the term cross-correlations is used for referring to the correlations between the entries of two random vectors X and Y, while the autocorrelations of a random vector X are considered to be the correlations between the entries of X itself, those forming the correlation matrix (matrix of correlations) of X. This is analogous to the distinction between autocovariance of a random vector and cross-covariance of two random vectors. One more distinction to point out is that in probability and statistics the definition of correlation always includes a standardising factor in such a way that correlations have values between −1 and +1.

上記は英語版 WikipediaCross-correlation から引用したものですが、調べものをする際は基本的に読み飛ばす癖があるので、この部分を見つけるまでにかなりの時間を要してしまいました。

引用によりますと、自己相関(autocorrelation)や相互相関(cross-correlation)と言った場合に統計学(statistics)および信号処理(signal processing)にて定義に若干の違いが有るようです。 統計学においては「相関(correlation)」と言った場合は常に正規化されており、値は -1 ~ 1 を取るのに対し、信号処理では「相関」と言った場合は処理対象の値をそのまま返し、正規化されたものは特別に「相関係数(correlation coefficient)」と呼ばれるようです。

要約すると正規化の有無により以下のような呼称の違いが有るようです。

正規化 統計学 (statistics) 信号処理 (signal processing)
× 相互共分散 (cross-covariance) 相互相関 (cross-correlation)
相互相関 (cross-correlation) 相互相関係数 (cross-correlation coefficient)
× 自己共分散 (autocovariance) 自己相関 (autocorrelation)
自己相関 (autocorrelation) 自己相関係数 (autocorrelation coefficient)

また統計学の定義では「共分散(covariance)」および「相互共分散(cross-covariance)」に数式的な違いが見いだせませんでしたが、これは概念的な違いを明確にするための言葉のようでした(英語版 Wikipedia Cross-covariance より引用)。

In the case of two random vectors X=(X_1, X_2, ... , X_n) and Y=(Y_1, Y_2, ... , Y_n), the cross-covariance would be a square n by n matrix C{XY} with entries C{XY}(j,k) = cov(X_j, Y_k).\, Thus the term cross-covariance is used in order to distinguish this concept from the "covariance" of a random vector X, which is understood to be the matrix of covariances between the scalar components of X itself.

実際の解析時に使用する numpy では信号処理的な定義が用いられているため(関数の返り値が正規化されていない)今回は信号処理の定義および呼称を用いることとします。

相互相関 (Cross-correlation)

相互相関は $f \star g$ と表され、連続関数に対する定義は次式で表されます。

$$
\begin{align}
(f \star g)(t) &\equiv \overline{f}(-t) * g(t) \\
&\equiv \int_{-\infty}^{\infty} \overline{f}(\tau) g(t + \tau) d\tau
\end{align}
$$

合成積の場合と同様に、離散的な集合に対して相互相関関数を考える場合は以下のように無限積を取ります。

$$
(a \star v)(n) \equiv \sum_{m=-\infty}^{\infty} \overline{a}_{m} v_{n + m}
$$

numpy では合成積と同様に離散値に対応したものが numpy.correlate(a, v) として定義されています。 この関数も numpy.convolute(a, v) と同様に、渡された一次配列 a および v の値域がオーバラップする範囲 (N + M - 1 when N := len(a), M := len(v))の相互相関関数の結果を返します。

自己相関 (Autocorrelation)

定義より自己相関は相互相関を用いて以下のように表すことができます。

$$
R(t) \equiv (f \star f)(t) \equiv \int_{-\infty}^{\infty} \overline{f}(\tau) f(t + \tau) d\tau
$$

また、離散的な集合に対しては

$$
R(n) \equiv (a \star a)(n) \equiv \sum_{m=-\infty}^{\infty} \overline{a}_{m} a_{n + m}
$$

とすることができます。

numpy では対応する関数は存在しませんが、上記の相互相関関数に同じ集合を渡すことで代用できます。

実装する

解析対象の定義

解析対象として 400, 800, 1280 Hz のサイン波を合成したシグナルを利用します。

import numpy as np
import matplotlib.pyplot as pl

# サンプルデータの定義 ========================================================
N = 250         # Signal length
fs = 1000       # Sampling rate
fn = fs * 0.5   # Nyquist frequency

t = np.arange(N) / fs
# 400 Hz
sine_400 = np.sin(2. * np.pi * 400 * t)
# 800 Hz
sine_800 = 2 * np.sin(2. * np.pi * 800 * t)
# 1280 Hz
sine_1280 = 2 * np.sin(2. * np.pi * 1280 * t)

y = sine_400 + sine_800 + sine_1280

pl.plot(t, y)

上記により以下のようなグラフが表示されます。

numpy.correlate を利用した場合

定義的には相互相関関数に対して同じ集合を割り当ててやれば自己相関関数になるはずです。 そこで、上記のサンプルデータを素直に割り当ててみます。

Rr = np.correlate(y, y, mode='full')
pl.plot(Rr)

先にも述べたとおり信号処理的な定義では正規化は行われていません。 そのため numpy.correlate 関数は

  • 定義域: (-∞, ∞)
  • 値域: (-∞, ∞)

となります。一方 Water survival probability など論文等で個人的によく見る自己相関関数は

  • 定義域: (0, ∞)
  • 値域: (-1;, 1)

となり(上記 Water survival probability に関して言えば)時間経過による相関の減衰を評価することができます。

解析ではこのようなグラフが欲しいことが多々有りますが、その場合は numpy.correlate() に対し

  • データポイントの集合を平均値からの差分集合(偏差集合)にする
  • データの散らばり具合(分散)で正規化する(※)
  • Rr(0) が定義上最大値なので、その値で正規化する

統計学的な定義では期待値(Expected value)が使われているため分散で正規化するだけで値域が -1 ~ 1 となりますが、信号処理的な定義では分散で (ここで日記は途切れている……

小指奮闘記 (Vimmer の Vimmer による Vimmer のためのキーマッピング)

どうも、サブタイトル通りご無沙汰しております有末です。 この記事は Vim Advent Calendar 2016 の 24 日目の記事となります。

小指の痛み

f:id:lambdalisue:20161224213005p:plain

VimEmacs など他のエディタとは違い、モード切り替えの概念があるため、修飾キーを押す機会は少なめです。 しかしながら、エディタにこだわりを持つほどのヘビーユーザーであればキーボードに触れている時間は長く、一般的なユーザーに比べ指の疲労がたまりがちかと思います。

かくいう僕も、近頃小指の疲労が如実に現れだしこれはヤバイと思ったため 小指をなるべく使用しない方法 を二ヶ月ほど試行錯誤してきました。 本日はその試行錯誤に関して、お試し難易度レベルごとに書いていきたいと思います。

Level 1. Vim 内で完結する

f:id:lambdalisue:20161224213446p:plain

まずは Vim の設定やプラグインだけで小指の使用頻度を下げてみましょう。

ノーマルモードにおける Ctrl/Shift の見直し

上記したとおりノーマルモードでは修飾キーを必要とする機会が少ないですが、以下のような機能はデフォルトで修飾キーを必要とします。

  • 数値のインクリメント・デクリメント (Ctrl-A, Ctrl-X)
  • 画面スクロール (Ctrl-B/F, Ctrl-D/U, Ctrl-E/Y)
  • ジャンプ (Ctrl-], Ctrl-^, Ctrl-O, Ctrl-I, Ctrl-T, W, F, E, T, G, $, ...)
  • 再描画 (Ctrl-L)
  • やり直し (Ctrl-R)
  • ブロック選択 (Ctrl-V)
  • ウィンドウ関連 (Ctrl-W)
  • インサートモードへの切り替え (I, A, O)
  • ペースト (P)
  • etc...

そこで、使用頻度が高いマッピングCtrl/ShiftSpace に割り当て直し、小指の代わりに親指を使うようなマッピングを定義しました。 なお、ノーマルモードにて Spacel と同じ機能 (カーソルを右に移動) が割当たっているため潰しても問題ありません (vimでキーマッピングする際に考えたほうがいいこと)。

" Ctrl/Shift の代わりに Space を利用する (使用感が大きく異なるため非推奨)
" バッティングするため個人的に使用頻度が高いものを優先的に割当ている
nnoremap <Space> <Nop>
" Shift
nnoremap <Space>a A
nnoremap c<Space>4 C
nnoremap d<Space>4 D
nnoremap <Space>f F
nnoremap <Space>g G
nnoremap <Space>j J
nnoremap <Space>k K
nnoremap <Space>n N
nnoremap <Space>p P
nnoremap <Space>4 $
nnoremap <Space>5 %

" Ctrl
nnoremap <Space>c <C-c>
nnoremap <Space>d <C-d>
nnoremap <Space>u <C-u>
nnoremap <Space>e <C-e>
nnoremap <Space>y <C-y>
nnoremap <Space>] <C-]>
nnoremap <Space>6 <C-^>
nnoremap <Space>o <C-o>
nnoremap <Space>i <C-i>
nnoremap <Space>t <C-t>
nnoremap <Space>l <C-l>
nnoremap <Space>r <C-r>
nnoremap <Space>v <C-v>
nnoremap <Space>ww <C-w>w
nnoremap <Space>wp <C-w>p
nnoremap <Space>wh <C-w>h
nnoremap <Space>wj <C-w>j
nnoremap <Space>wk <C-w>k
nnoremap <Space>wl <C-w>l

なお、コメントにも書いてある通り上記のマッピングは非推奨です。 試してみればわかるかと思いますが Space は修飾キーではないため押しっぱなし系のマッピングには使えません。 例えば、画面を下にスクロールする場合は Ctrl を押したまま d を連打することでスクロールが出来ますが、上記のマッピングでは Space を押したまま d を連打すると最初の一回のみスクロールが発生し、残りはすべて dd の機能である行削除が発生します

これではマトモに使えないため kana/vim-submode というプラグインの力を借りることにします。

github.com

vim-submode は 特定のマッピングによりユーザーが独自に追加したモードに入ることができるプラグイン です。 上記のスクロールの例では「Space-d でスクロールモード (独自) に入り、スクロールモード内で d を押した場合は下にスクロールする」というように定義してあげると Ctrl-d の使用感を模倣することが出来ます。 そのため、以下のようなマッピングを定義すると Ctrl の使用感を大きく変えずに Space を代わりに利用することが出来ます。

" NOTE: vim-submode はインストール済みと仮定

" Ctrl/Shift の代わりに Space を利用する (vim-submode 必須)
" バッティングするため個人的に使用頻度が高いものを優先的に割当ている
nnoremap <Space> <Nop>

" J
call submode#enter_with('J', 'n', '', '<Space>j', 'J')
call submode#map('J', 'n', '', 'j', 'J')

" N
call submode#enter_with('N', 'n', '', '<Space>n', 'N')
call submode#map('N', 'n', '', 'n', 'N')

" %
call submode#enter_with('%', 'n', '', '<Space>5', '%')
call submode#map('%', 'n', '', '5', '%')

" Ctrl-d/u -> Ctrl-b/f/d/u/e/y
call submode#enter_with('scroll', 'n', '', '<Space>d', '<C-d>')
call submode#enter_with('scroll', 'n', '', '<Space>u', '<C-u>')
call submode#map('scroll', 'n', '', 'b', '<C-b>')
call submode#map('scroll', 'n', '', 'f', '<C-f>')
call submode#map('scroll', 'n', '', 'd', '<C-d>')
call submode#map('scroll', 'n', '', 'u', '<C-u>')
call submode#map('scroll', 'n', '', 'e', '<C-e>')
call submode#map('scroll', 'n', '', 'y', '<C-y>')

" Ctrl-]/^/o/i/t
call submode#enter_with('jump', 'n', '', '<Space>]', '<C-]>')
call submode#enter_with('jump', 'n', '', '<Space>6', '<C-^>')
call submode#enter_with('jump', 'n', '', '<Space>o', '<C-o>')
call submode#enter_with('jump', 'n', '', '<Space>i', '<C-i>')
call submode#enter_with('jump', 'n', '', '<Space>t', '<C-t>')
call submode#map('jump', 'n', '', ']', '<C-]>')
call submode#map('jump', 'n', '', '6', '<C-^>')
call submode#map('jump', 'n', '', 'o', '<C-o>')
call submode#map('jump', 'n', '', 'i', '<C-i>')
call submode#map('jump', 'n', '', 't', '<C-t>')

" Ctrl-r
call submode#enter_with('redo', 'n', '', '<Space>r', '<C-r>')
call submode#map('redo', 'n', '', 'r', '<C-r>')

" Shift
nnoremap <Space>a A
nnoremap c<Space>4 C
nnoremap d<Space>4 D
nnoremap <Space>f F
nnoremap <Space>g G
nnoremap <Space>k K
nnoremap <Space>p P
nnoremap <Space>4 $

" Ctrl
nnoremap <Space>c <C-c>
nnoremap <Space>l <C-l>
nnoremap <Space>v <C-v>
nnoremap <Space>ww <C-w>w
nnoremap <Space>wp <C-w>p
nnoremap <Space>wh <C-w>h
nnoremap <Space>wj <C-w>j
nnoremap <Space>wk <C-w>k
nnoremap <Space>wl <C-w>l

vim-submode が正常にインストールされていれば上記によりほぼ Ctrl/Shift と使用感を変えずに Space を使うことが出来、小指の使用頻度削減に貢献できます。 なお vim-submode に関してより詳しく知りたい場合は :help submode や以下を参照してください。

インサートモードにおける Shift キー (Sticky shift / Software Capslock)

インサートモードでは大文字の入力に Shift を利用するため小指の疲弊につながります。 しかし、ノーマルモードと違い Space のような打ちやすく潰しても問題ないキーが無いため、上記のような再割当ては難しく仕方なく Shift を打つことが多いと思います。

この問題に対しての解決策として Sticky shift という方法があります。 Sticky shift を設定すると「粘着キー」の次に押されたキーが Shift と同時に押されたように振る舞います。 具体的には粘着キーを ; に割り当てた場合に ;a と入力すると A が入力されるという形です(HogePiyo と入力する場合は ;hoge;piyo と入力)。

Sticky shift を導入すれば大文字を入力する際に Shift を押しっぱなしにする必要が無いため、小指に対する負荷を若干下げることが出来ます。 また、上記のように粘着キーを ; に割り当てれば負荷が右手の小指/薬指に分散されるため左 Shift を多用するタイプの人であれば左手小指の負荷を大幅に削減することが出来ます。

この Sticky shift を Vim で実現する方法に関しては Hack #54: VimでSticky Shiftを実現する が詳しいですが、ここに 記載されている方法ではノーマルモードでの r による一文字置き換え時に利用できないという欠点や連続で大文字を入力する場合に面倒という欠点があります。 その欠点も補ったものを lambdalisue/pinkyless.vim というプラグインとして作成したので Sticky shift を導入してみたい方はご利用ください。

github.com

上記プラグインをインストールするだけで以下の機能を提供します

  • インサートモード時に ; を押すと Sticky mode に入る (Sticky shift)
    • デフォルトではコマンドラインでも利用可能
    • 次に押されたキーが Shift と同時に押されたように振る舞う
    • ; 自体を入力したい場合は Space を続けて入力する
    • ; と改行を入力したい場合は Enter を続けて入力する
  • ノーマルモードで r を押したときに入る一文字置換モードでも上記のルールが適用される
  • インサートモードにて大文字を連続入力したい場合は F10 を押すと CapsLock がかかった状態を模倣する (Software Capslock)
    • デフォルトではコマンドラインでも利用可能
    • 再度 F10 を押すと戻る
    • ハード的な CapsLock では無いためインサートモードを抜けると自動的に戻る

なお、上記記事およびプラグイン双方 US 配列を基準としているので JIS 配列をご利用の方はご注意ください。

Level 2. Vim の外でも Sticky shift 対応する

f:id:lambdalisue:20161224213557p:plain

Vim 以外のエディタにはモードという概念があるわけではないため Ctrl/ShiftSpace に置き換えるようなアグレッシブな方法を取ることは出来ませんが、最後に紹介した Sticky shift に関しては対応できます。 Vim Adevent Calendar の記事ですが、ここからはほとんど Vim の話は出てきません。

Mac OS X の場合

Mac OS X の場合は Karabiner を利用することで Sticky shift を Vim 外で実現することが出来ます。

Karabiner - OS X用のソフトウェア

なお上記 Karabiner は 2016/12/24 現在 macOS Sierra には対応しておらず、僕が知る限り代替手段もないため下記の方法は Mac OS X Elcapitan までの方法となります。

Karabiner にはデフォルトで ; を修飾キーとする Stick shift が提供されていますが、以下のような問題点があります。

  • ;; とした場合でも : とならず : を入力する方法を別に用意する必要がある
  • ; とした場合でも ; とならず ; を入力する方法を別に用意する必要がある
  • ; を長押しした場合は Shift として扱われる

これらの問題点は上記で紹介した pinkyless.vim と使用感が異なり、個人的にはとても使いにくかったため独自に private.xml に定義しました。 private.xml$HOME/Library/Application Support/Karabiner/private.xml にあるため、ファイルを直接開いて編集するか Karabiner 側から private.xml の編集ボタンを押し、以下を定義します。

<?xml version="1.0"?>
<root>
  <item>
    <name>Sticky shift with semicolon (;)</name>
    <appendix>Use double semicolon (;;) to insert a colon (:)</appendix>
    <appendix>Use semicolon + space (; ) to insert a semicolon (;)</appendix>
    <identifier>private.pinkyless_vim_sticky_shift_with_semicolon</identifier>
    <autogen>
    __SimultaneousKeyPresses__
    KeyCode::SEMICOLON, KeyCode::SPACE,
    KeyCode::SEMICOLON
    </autogen>
    <autogen>
    __DoublePressModifier__
    KeyCode::SEMICOLON,
    KeyCode::VK_STICKY_SHIFT_L,
    KeyCode::SEMICOLON, ModifierFlag::SHIFT_L
    <!-- JIS の場合上記を KeyCode::JIS_COLON に変更する -->
    </autogen>
  </item>
</root>

ファイル保存後 Karabiner 側で Reload を行い「Sticky shift with semicolon (;)」にチェックを入れると pinkyless.vim と使用感が似た Sticky shift をどこでも利用できるようになります。 なお __SimultaneousKeyPresses__ はデフォルトで、ほぼ同時押しを行わないと認識されないようになっているので ; を入力する際は ;Space を同時押ししてください。 また上記のマッピングを行うと先の pinkyless.vim とバッティングするため pinkyless.vim を無効化するか上記マッピングVim では利用しないように設定して下さい(下記リンク参照)。

Linux の場合

Linux の場合は xkb の LatchMods 機能を利用することで Sticky shift を実現することが出来、デフォルトでは ISO_Level2_Latch という名の Sticky shift が iso9995 ファイルにて提供されています。 ただ、僕の勉強不足で pinkyless.vim や Karabiner の時のように ; を Sticky shift の粘着キーとして利用する方法に関しては調べきれていません。 そのため使用感はかなり異なりますが、左手小指の負荷を下げるために右シフトに上記 ISO_Level2_Latch を割り当てます。

設定ファイルの置き場所などの細かい話は下記リンク先に譲るとして、以下に右 Shift を Sticky shift として利用するための設定を記載します。

// $HOME/.config/xkb/symbols/my
partial modifier_keys
xkb_symbols "sticky" {
  // NOTE: ISO_Level2_Latch = Sticky shift
  replace key <RTSH> { [ ISO_Level2_Latch ] };
  modifier_map Shift { <RTSH> };
};

// $HOME/.config/xkb/keymap/my
// Usage: xkbcomp -I$HOME/.config/xkb ~/.config/xkb/keymap/my $DISPLAY
// US 配列を想定しているので JIS の方は注意
xkb_keymap {
  xkb_keycodes { include "evdev+aliases(qwerty)" };
  xkb_types    { include "complete" };
  xkb_compat   { include "complete+ledcaps(shift_lock)+ledscroll(scroll_lock)+lednum(num_lock)" };
  xkb_symbols  { include "pc+inet(evdev)+my(sticky)" };
  xkb_geometry { include "pc(pc104)" };
};

これにより右 Shift が Sticky shift として利用できるようになるはずです(執筆には Mac OS X を利用しているため動作未確認)。

Windows の場合

Google 先生にお聞きください。

Level 3. 親指に仕事をさせる

f:id:lambdalisue:20161224213640p:plain

上記をすべて行っても以下のような問題が発生しました

  • Sticky shift は一定の効果はあるが、プログラミングで多用する ;: の入力に 2 ステップ必要なのは面倒くさい
  • そもそも OS のショートカットで Ctrl などは多用されているため Vim だけで解決しても辛い

そこで根本的に考え方を変え、いつもスペースあたりでふらついている親指にキーボード配列レベルで仕事をさせることに決めました。 この考え自体は小指が痛くなり始めてから常にあったのですが、以下のような懸念があり中々実行には移せませんでした。

  1. 特殊なキー配列に慣れてしまうと、通常配列のキーボードが使えない
  2. 特殊なキー配列で利用しているとペアプロなど他人が利用する場合に混乱が生じる
  3. そもそも US 配列ではスペースが大きく親指に仕事をさせることが困難

ただ、上記に関しては以下のように考え直すことで解決できると判断しました。

  1. 基本的に自分が利用するパソコンは固定されているので、一度設定してしまえば良い。 まれに他人の PC を使う場合もあるが他人のPC利用時に最高効率で作業する必要はない。 そもそも Vimプラグインを多量に入れて使ってるので特殊な環境に対する恐怖自体が無意味
  2. ペアプロはしない。する場合も Mac OS X なので Karabiner のプロファイル機能にて特殊なキー配列設定を無効化すれば良い
  3. US 配列の利点は記号の論理性がメインのため JIS 配列を US 配列として利用すれば良く、その場合変換無変換キー (かな英語キー) が利用できる。 また US 配列は Enter が近いという利点も存在するが、以下に紹介するように親指に Enter を割り当てれば解決する。

上記を踏まえた上で、以下のようなキー配列を JIS Pinkyless 配列として定義しました。

f:id:lambdalisue:20161224224652p:plain http://www.keyboard-layout-editor.com/#/gists/678ab6fc95f2af3c66da1e89dc3bfede

上記のキー配列は基本的に Mac JIS 配列上で US 配列を模倣していますが、以下の点が異なります。

  • A の隣は Control (Mac JIS の場合はそのまま、PC JIS の場合は置き換え)
  • 左下は Ctrl (Mac JIS の場合は置き換え、PC JIS の場合はそのまま)
  • および \ の位置を交換
    • 後述する ErgoDox Pinkyless 配列と合わせるため
    • / の反対である \ が隣にくるため覚えやすい
    • プログラミングにおいて '" に近い使用用途の が隣に来るため覚えやすい
  • PC キーの Windows (L) および Alt (L) の位置に Command (L) および Option (L) キーを移動。
    • 後述する ErgoDox Pinkyless 配列が PC 配列をベースとしているため
  • Space はマルチファンクショナルキーに設定
    • Space のみを押した場合は Space として利用
    • 他のキーと同時押しした場合は Ctrl として利用
  • かな (変換) はマルチファンクショナルキーに設定
    • かな のみを押した場合は Enter として利用
    • 他のキーと同時押しした場合は Shift として利用
  • 英数 (無変換) はスペースを連続して入力する際に利用するため Space として利用
  • Command (R) (カタカナ) は改行を連続して入力する際に利用するため Enter として利用
  • Shift (L)英数 (無変換) とし IME の OFF に利用
  • Shift (R)かな (変換) とし IME の ON に利用

上記のキー配列は慣れるまで多少の時間がかかりますが、おかげさまで小指の使用頻度が劇的に減りました。 また、マッピング自体を大幅に変更したため上記で説明してきたような小技(Space を代わりに利用・Sticky shift)などは完全に不要になりました。

上記を試してみたい場合は以下の設定をご利用ください。

Mac OS X の場合

以下を参考に private.xml<item> を追加してください。 なお Karabiner では Capslock に機能を割り当てられないため Mac OS X 自体の機能を使うか Seil を使います。 また JIS 配列を US 配列として使う機能は Karabiner にデフォルトで存在するため、そちらを利用します

<?xml version="1.0"?>
<root>
  <item>
    <name>For Mac JIS Keyboard</name>
    <item>
      <name style="important">The following extra configurations are required.</name>
      <appendix><![CDATA[ * Assign "No Action" to Capslock by in Keyboard > Modifier keys... > Caps Lock ]]></appendix>
      <appendix><![CDATA[ * Assign "59" (CONTROL_L: 0x37) to Capslock in "Seil". ]]></appendix>
      <appendix>&#8594; https://pqrs.org/osx/karabiner/seil.html</appendix>
    </item>
    <item>
      <name>Pinkyless : JIS Keyboard (Mac) to US Keyboard (Mac)</name>
      <appendix><![CDATA[  * Option_L                 -> Command_L ]]></appendix>
      <appendix><![CDATA[  * Command_L                    -> Option_L ]]></appendix>
      <appendix><![CDATA[  * Eisuu                        -> Space ]]></appendix>
      <appendix><![CDATA[  * Space                        -> Control_L/Space ]]></appendix>
      <appendix><![CDATA[  * Kana                     -> Shift_R/Enter ]]></appendix>
      <appendix><![CDATA[  * Command_R                    -> Enter ]]></appendix>
      <appendix><![CDATA[  * Shift_L                      -> Eisuu ]]></appendix>
      <appendix><![CDATA[  * Shift_R                      -> Kana ]]></appendix>
      <identifier>private.macjis_to_macus_pinkyless</identifier>
      <autogen>__KeyToKey__ KeyCode::OPTION_L, KeyCode::COMMAND_L</autogen>
      <autogen>__KeyToKey__ KeyCode::COMMAND_L, KeyCode::OPTION_L</autogen>
      <autogen>__KeyToKey__ KeyCode::COMMAND_R, KeyCode::RETURN</autogen>
      <autogen>__KeyToKey__ KeyCode::JIS_EISUU, KeyCode::SPACE</autogen>
      <autogen>__KeyToKey__ KeyCode::SHIFT_L, KeyCode::JIS_EISUU</autogen>
      <autogen>__KeyToKey__ KeyCode::SHIFT_R, KeyCode::JIS_KANA</autogen>
      <autogen>
        __KeyOverlaidModifier__
        KeyCode::SPACE,
        KeyCode::CONTROL_L,
        KeyCode::SPACE
      </autogen>
      <autogen>
        __KeyOverlaidModifier__
        KeyCode::JIS_KANA,
        KeyCode::SHIFT_R,
        KeyCode::RETURN
      </autogen>
      <autogen>__KeyToKey__ KeyCode::JIS_UNDERSCORE, KeyCode::BACKSLASH</autogen>
      <autogen>__KeyToKey__ KeyCode::BACKSLASH, KeyCode::BACKQUOTE</autogen>
    </item>
  </item>
</root>

なお外付けの 109 キーボード (PC JIS) を利用している場合は以下をご利用ください。

<?xml version="1.0"?>
<root>
  <item>
    <name>For PC JIS Keyboard</name>
    <item>
      <name style="important">The following extra configurations are required.</name>
      <appendix><![CDATA[ * Enable NFER (102 : JIS eisuu) in "Seil". ]]></appendix>
      <appendix><![CDATA[ * Enable XFER (104 : JIS kana) in "Seil". ]]></appendix>
      <appendix><![CDATA[ * Enable KATAKANA (54 : Right command) in "Seil". ]]></appendix>
      <appendix>&#8594; https://pqrs.org/osx/karabiner/seil.html</appendix>
    </item>
    <item>
      <name>Pinkyless : JIS Keyboard (PC) to US Keyboard (Mac)</name>
      <appendix><![CDATA[  * Muhenkan (Eisuu)             -> Space ]]></appendix>
      <appendix><![CDATA[  * Space                        -> Control_L/Space ]]></appendix>
      <appendix><![CDATA[  * Henkan (Kana)                -> Shift_R/Enter ]]></appendix>
      <appendix><![CDATA[  * Katakana (Command_R)         -> Enter ]]></appendix>
      <appendix><![CDATA[  * Shift_L                      -> Eisuu ]]></appendix>
      <appendix><![CDATA[  * Shift_R                      -> Kana ]]></appendix>
      <appendix><![CDATA[  * Application                  -> Fn ]]></appendix>
      <identifier>private.pcjis_to_macus_pinkyless</identifier>
      <autogen>__KeyToKey__ KeyCode::SHIFT_L, KeyCode::JIS_EISUU</autogen>
      <autogen>__KeyToKey__ KeyCode::SHIFT_R, KeyCode::JIS_KANA</autogen>
      <autogen>__KeyToKey__ KeyCode::PC_APPLICATION, KeyCode::FN</autogen>
      <autogen>__KeyToKey__ KeyCode::COMMAND_R, KeyCode::RETURN</autogen>
      <autogen>__KeyToKey__ KeyCode::JIS_EISUU, KeyCode::SPACE</autogen>
      <autogen>
        __KeyOverlaidModifier__
        KeyCode::SPACE,
        KeyCode::CONTROL_L,
        KeyCode::SPACE
      </autogen>
      <autogen>
        __KeyOverlaidModifier__
        KeyCode::JIS_KANA,
        KeyCode::SHIFT_R,
        KeyCode::RETURN
      </autogen>
      <autogen>__KeyToKey__ KeyCode::JIS_UNDERSCORE, KeyCode::BACKSLASH</autogen>
      <autogen>__KeyToKey__ KeyCode::BACKSLASH, KeyCode::BACKQUOTE</autogen>
    </item>
  </item>
</root>

Linux の場合

後述しますが、執筆段階では Linux にて JIS Pinkyless 配列を利用していないため xkb で頑張ってください。

Windows の場合

Google(ry

Level MAX. セパレートタイプのキーボードを使う

f:id:lambdalisue:20161224232634p:plain

ここまで来ると、もう引き返せませんね。

このレベルへと達するにあたり、いろいろと試行錯誤をしてきましたが、結局のところ物理的に親指で押しやすい位置にキーが有るものがベストのため ErgoDox EZ を購入しました。

ErgoDox がいかに素晴らしいかという紹介は先人に任せるとして、ここでは僕が利用しているマッピングマッピングを決定する際に注意した点に関して述べたいと思います。 以下が現在僕が利用している ErgoDox Pinkyless マッピングとなります。

f:id:lambdalisue:20161224224833p:plain http://www.keyboard-layout-editor.com/#/gists/87442658ad18f4d065e49573967dd1b5

上記マッピングでは以下のような点に注意して配置を決めています。

  • 可能な限り US 配列の使用感を大切にする
  • Ctrl は単体利用はしない + Space 長押しの頻度は低いため複合キーとし、最も押しやすい内側の親指位置に配置(左手)
  • Space 長押しが必要な場合も稀にあるため、外側の親指位置に Space 単体を配置(左手)
  • Shift は単体利用はしない + Enter 長押しの頻度は低いため複合キーとし、最も押しやすい内側の親指位置に配置(右手)
  • Enter 長押しが必要な場合も稀にあるため、外側の親指位置に Enter 単体を配置(右手)
  • IME の ON/OFF は現在の IME の状態を意識したくないため Mac英数/かな を模倣して別キーとした。 また JIS Pinkyless ではキーの数と押しやすさの関係から左右の Shift を割り当てるため、近い位置である左右の Shift の下に配置
  • +- と使用感が近いため - の下に = を配置
  • [] はセットだが P の横にはキーが一つしか無い (そのうえ = を配置している) ため左右でわかりやすい形に配置
  • "Vim においてレジスタで利用するため US 配列と同様に ; の横に ' を配置
  • \' の横にキーが空いていないため \ と似た / の横に配置
  • ~ の使用頻度が比較的高いため Shift とは逆の左手の開いている場所に配置 (Shift は右手親指で押すため)

なお、僕の利用している ErgoDox Pinkyless マッピングに興味を持たれた方は以下がファームウェアのコードになりますので自己責任にてご利用ください。

github.com

また、デフォルトの qmk_firmware では MT(mod, kc) の仕様上タップ -> ホールドという動作により連続タップが送られるようになっていますが、上記のように CtrlShift を割り当てた場合ホールド時は必ずホールド動作をしてくれる方が使用感が良いです。 上記のファームウェアを利用して CtrlShift の使用感に困った場合は以下の issue に修正 patch をコメントしてあるので、参照してください。

github.com

ちなみに Level 3. で定義した JIS Pinkyless 配列ですが、これは僕が ErgoDox にて利用している ErgoDox Pinkyless 配列と使用感を似せるように作られています。 そのため ErgoDox が無い外出先では JIS Pinkyless 自宅・職場では ErgoDox Pinkyless と、どのような環境でも親指で Ctrl/Shift が使えるようになっています。

おわりに

現在は Level 1, 2 で紹介した小手先な手法ではなく JIS Pinkyless および ErgoDox Pinkyless 配列を使用することで小指にも優しい Vimmer を目指しております。 本日の記事は若干の Vim 要素と Vim 愛により突き動かされたキーボードカスタマイズの話でしたが、明日は Vim 界の巨匠  Koron さんの記事です。全裸待機して待ちましょう! (Kolon とタイポしていました。謹んでお詫び申し上げます orz)

メモ:連番ファイルのリネーム

少しご無沙汰しております。有末です。

超便利な rename コマンドというのがありますが、これで指定しているの Perl正規表現だったんですね。 なので e 指定して Perl の式として評価させてあげると連番ファイルの数値を計算しながらリネームできます(CentOS とかは同じ名前の違うコマンドなので不可)。

rename 's/foo(\d+)\.txt/"bar" . ($1 - 1) . "txt"/e' *.txt -n
foo1.txt renamed as bar0.txt
foo2.txt renamed as bar1.txt
foo3.txt renamed as bar2.txt

とても便利。

参考:

正規表現(10) 置換オプション /e - ぱるも日記

あんまり見かけない気がする Vim の Tips 11 + 1 選

どうも、ご無沙汰しておりません Vimmer + Pythonista、 略して Vist... やめよう、こっちは縁起が悪い。 この記事は Vim Advent Calendar 2015 の 24日目の記事となります。

はじめに

自分が Mac OS X および Linux しか持っていないため、Windows で動かなかったらごめんなさい。 とりあえず書こうと思っていたものが未完成なので、他ではあまり見たことがない ~/.vimrc のお便利設定をまとめます。

続きを読む

Jupyter (旧 IPython notebook)の本来あるべき姿を垣間見る

どうも、ご無沙汰しております Pythonista + Vimmer、略して Pythonimmer な有末です。 この記事は Python その2 Advent Calendar 2015 22 日目の記事となります。

Jupyter とは

さて、皆様は Jupyter(旧 IPython notebook) というプロジェクトをご存知でしょうか? Jupyterがすごい勢いでやってくるからお前ら備えとけ(IPython Notebook + R)Railsエンジニアに役立つJupyter NotebookとiRuby に代表されるように、現在各界から注目されているプロジェクトとなっております

簡単に言うと Web ブラウザ上にて Markdown および任意の言語の記述・実行・表示を行うサービス となります。 科学者が使う実験ノートにインスパイアされたプロジェクトであることから、主に実験結果の解析スクリプトなどで真価を発揮し、以下の様な利点があります。

  • 一連の解析の各ステップを一つのファイルのセクション単位で記載することで、後日確認した時に流れがわかりやすい
  • 解析の過程で必ず存在する「思考過程」や「結果・考察」などを Markdown にて記載可能なので、ソースコードでは表現が難しい概念なども画像などのリソースを用いてわかりやすくまとめることができる
  • matplotlib に代表されるグラフ描画ライブラリのインライン展開に対応しており、ソースコードの実行結果を同じ画面にて確認可能(下記スクリーンショット参照)

通常のプログラムではなく、一過的なスクリプトを書く場合は非常に便利なソフトウェアとなっております。 Try Jupyter にて簡単に試すことが出来るので、ご存じなかった方は一度お試しください。

f:id:lambdalisue:20151222211842p:plain

Jupyter にて Python を実行し解析結果をグラフで表示している様子

続きを読む