ぼろぼろ平原

困った

gensim.corpora.Dictionaryの単語IDを辞書順にする

gensimのcorpora.Dictionaryは単語IDが適当に振られてしまい困るのでSortableDictionaryを作った。

from gensim import corpora

class SortableDictionary(corpora.Dictionary):
    def __init__(self, *arg, **kwargs):
        super().__init__(*arg, **kwargs)

    def sort(self):
        idmap = dict(zip((v for k, v in sorted(self.token2id.items())), range(len(self.token2id))))
        self.token2id = dict((token, idmap[tokenid]) for token, tokenid in self.token2id.items())
        self.id2token = {}
        self.dfs = dict((idmap[tokenid], freq) for tokenid, freq in self.dfs.items())


# corpora.Dictionary(documents) としていたところをこのように置き換える
dictionary = SortableDictionary(documents)

# 辞書順にソートしたいときにsort()を呼び出す
dictionary.sort()

# 元のcorpora.Dictionaryと同じようにファイルに保存できる
dictionary.save_as_text("foo.txt")

# 保存したファイルは元のcorpora.Dictionaryでも読み込める
dictionary = corpora.Dictionary.load_from_text("foo.txt")

Pythonで大きいファイルを読み込む時に進捗を表示

この記事のPython版。

http://tatzyr.hatenablog.com/entry/2016/07/13/144906

tatzyr.hatenablog.com

進捗を表示するためには、ファイル全体の行数かファイルサイズが必要になる。

ファイルの行数を取得するにはファイルを一度全部読み込む必要があり、大きいファイルだと時間がかかるのでダメ。

一方、ファイルのサイズはos.path.getsizeを使えば一瞬で取得できる。 また、組み込み関数のlenを使うと読み込んだ読み込んだオブジェクトの長さが取得できるので、現在の進捗が計算できる。

プログレスバーの表示には tqdm を使う。

tqdmのインストール

$ pip install tqdm

進捗を表示するプログラム

from tqdm import tqdm
import os

# 大きいファイルのファイル名
filename = "sugoku_dekai_file.txt"

# ファイルのサイズを取得
filesize = os.path.getsize(filename)

# ファイルを開く
with open(filename) as f:
    # ファイルのサイズを使用しプログレスバーを作成
    with tqdm(total=filesize) as pbar:
        # 行ごとに処理
        for line in f:
            # 読み込んだ行をbytesに変換して長さを取得
            read_size = len(line.encode('utf-8'))
            # lineに対して何か処理
            foo(line)
            # 進捗を更新
            pbar.update(size)

MeCabをparseしやすいLTSVで出力

MeCabの出力はデフォルトで以下のようになるが、 これが意外と扱いづらい。

$ echo 新しいスクエニのゲームを買った。 | mecab
新しい   形容詞,自立,*,*,形容詞・イ段,基本形,新しい,アタラシイ,アタラシイ
スクエニ    名詞,一般,*,*,*,*,*
の 助詞,連体化,*,*,*,*,の,ノ,ノ
ゲーム   名詞,一般,*,*,*,*,ゲーム,ゲーム,ゲーム
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
買っ  動詞,自立,*,*,五段・ワ行促音便,連用タ接続,買う,カッ,カッ
た 助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
。 記号,句点,*,*,*,*,。,。,。
EOS

扱いづらい理由

扱いづらい理由はいろいろある。

  • まずTABで区切って、さらにカンマで区切らないといけない
  • 未知語だけカンマの数が違う(上の例だと「スクエニ」の行だけカンマが少ない)
  • *が邪魔
  • EOSが邪魔
  • 形態素の原型はインデックス6?それともインデックス7?」みたいにたまに混乱する

LTSV形式での出力

MeCabのオプションを変更すると、以下のようにLTSV形式で出力できる。

$ echo 新しいスクエニのゲームを買った。 | mecab -F "surface:%m\tpos:%f[0]\tpos1:%f[1]\tpos2:%f[2]\tpos3:%f[3]\tcform:%f[4]\tctype:%f[5]\tbase:%f[6]\tread:%f[7]\tpron:%f[8]\n" -U "surface:%m\tpos:%f[0]\tpos1:%f[1]\tpos2:%f[2]\tpos3:%f[3]\tcform:%f[4]\tctype:%f[5]\tbase:%f[6]\tread:\tpron:\n" -E ""
surface:新しい   pos:形容詞   pos1:自立 pos2:   pos3:   cform:形容詞・イ段    ctype:基本形 base:新しい  read:アタラシイ    pron:アタラシイ
surface:スクエニ    pos:名詞  pos1:一般 pos2:   pos3:   cform:  ctype:  base:   read:   pron:
surface:の pos:助詞  pos1:連体化  pos2:   pos3:   cform:  ctype:  base:の    read:ノ    pron:ノ
surface:ゲーム   pos:名詞  pos1:一般 pos2:   pos3:   cform:  ctype:  base:ゲーム  read:ゲーム  pron:ゲーム
surface:を pos:助詞  pos1:格助詞  pos2:一般 pos3:   cform:  ctype:  base:を    read:ヲ    pron:ヲ
surface:買っ  pos:動詞  pos1:自立 pos2:   pos3:   cform:五段・ワ行促音便  ctype:連用タ接続   base:買う read:カッ pron:カッ
surface:た pos:助動詞   pos1:   pos2:   pos3:   cform:特殊・タ  ctype:基本形 base:た    read:タ    pron:タ
surface:。 pos:記号  pos1:句点 pos2:   pos3:   cform:  ctype:  base:。    read:。    pron:。

LTSVのparse

ltsv.orgを見れば分かる通り、LTSV形式は特別なライブラリ無しで簡単にparseできる。

Pythonの場合(-Eに空文字を指定する方法が無いのでEOSを手動で除去してる):

#!/usr/bin/env python

# pip install mecab-python3
import MeCab

mecab = MeCab.Tagger(r'-F surface:%m\tpos:%f[0]\tpos1:%f[1]\tpos2:%f[2]\tpos3:%f[3]\tcform:%f[4]\tctype:%f[5]\tbase:%f[6]\tread:%f[7]\tpron:%f[8]\n -U surface:%m\tpos:%f[0]\tpos1:%f[1]\tpos2:%f[2]\tpos3:%f[3]\tcform:%f[4]\tctype:%f[5]\tbase:%f[6]\tread:\tpron:\n')
s = mecab.parse("新しいスクエニのゲームを買った。").rstrip("EOS\n")
for line in s.splitlines():
    d = {k: v for k, v in [f.split(":", 2) for f in line.rstrip("\n").split("\t")]}
    print(d)

### 出力
# {'surface': '新しい',
#  'pos': '形容詞',
#  'pos1': '自立',
#  'pos2': '',
#  'pos3': '',
#  'cform': '形容詞・イ段',
#  'ctype': '基本形',
#  'base': '新しい',
#  'read': 'アタラシイ',
#  'pron': 'アタラシイ'}
# ...

Rubyの場合:

#!/usr/bin/env ruby

# gem install natto
require "natto"

mecab = Natto::MeCab.new('-F surface:%m\tpos:%f[0]\tpos1:%f[1]\tpos2:%f[2]\tpos3:%f[3]\tcform:%f[4]\tctype:%f[5]\tbase:%f[6]\tread:%f[7]\tpron:%f[8]\n -U surface:%m\tpos:%f[0]\tpos1:%f[1]\tpos2:%f[2]\tpos3:%f[3]\tcform:%f[4]\tctype:%f[5]\tbase:%f[6]\tread:\tpron:\n')

mecab.parse("新しいスクエニのゲームを買った。") do |line|
  unless line.is_eos?
    h = line.feature.chomp.split("\t").map {|f| f.split(":", 2) }.to_h
    p h
  end
end

### 出力
# {"surface"=>"新しい",
#  "pos"=>"形容詞",
#  "pos1"=>"自立",
#  "pos2"=>"",
#  "pos3"=>"",
#  "cform"=>"形容詞・イ段",
#  "ctype"=>"基本形",
#  "base"=>"新しい",
#  "read"=>"アタラシイ",
#  "pron"=>"アタラシイ"}
# ...

keyの名前について

LTSVのkeyにはこういう名前を付けた。

  • surface: 表層形
  • pos: 品詞
  • pos1: 品詞細分類1
  • pos2: 品詞細分類2
  • pos3: 品詞細分類3
  • cform: 活用形
  • ctype: 活用型
  • base: 原形
  • read: 読み
  • pron: 発音

Touch Bar付きのMacBook ProでMinecraftをプレイするときに常にファンクションキーを表示する方法

MacBook Pro (Late 2016) Touch Bar搭載モデルでMinecraftをプレイするときに、ファンクションキーを表示しておく方法です。

f:id:tatzyr:20161120234004g:plain

手順

  1. Minecraftを起動する
  2. Dockにある作業台アイコンをcommandキーを押しながらクリックするf:id:tatzyr:20161120222205p:plain
  3. Finderでjavaが選択されるので、右クリックして[“java"をコピー]をクリックするf:id:tatzyr:20161120213927p:plain
  4. Minecraftを終了させる
  5. ターミナルをLaunchpadから起動する。(Launchpadで「te」と検索すると見つかる)f:id:tatzyr:20161120213555p:plain
  6. ターミナルでcommand+Vを押す。すると、 /Applications/Minecraft.app/Contents/runtime/jre-x64/1.8.0_74/bin/javaのような文字列が貼り付けられるf:id:tatzyr:20161120214533p:plain
  7. 先頭にcd を追加(cdの後のスペースも重要!)して、末尾の/javaを削除するf:id:tatzyr:20161120214549p:plain
  8. Enterキーを押して実行する
  9. 以下のコマンドを順に入力し、Enterキーを押して実行する
    • mv java java.app
    • ln -s java.app java f:id:tatzyr:20161120214856p:plain
  10. [システム環境設定]→[キーボード]→[ショートカット]→[ファンクションキー]→[+ボタン]をクリックする f:id:tatzyr:20161120221134p:plain
  11. 出てきたウインドウに、2で開いたFinderにある「java」をドラッグアンドドロップする(ショートカットの矢印がついていないほう) f:id:tatzyr:20161120224533p:plain
  12. 追加ボタンをクリックする

元に戻す手順

  1. 上の手順の8までを実行する
  2. 以下のコマンドを入力し、Enterキーを押して実行する
    • mv java.app java
  3. [システム環境設定]→[キーボード]→[ショートカット]→[ファンクションキー]→[−ボタン]をクリックする

以上です。

関連記事

tatzyr.hatenablog.com

tatzyr.hatenablog.com

エスケープ文字を解釈してくれないコマンドに\nや\tを渡す方法

echoコマンドは-eオプションを付けないと、\n\tのようなエスケープ文字が有効にならない。

### -e なし
$ echo 'foo\nbar\tbaz'
foo\nbar\tbaz

### -e あり
$ echo -e 'foo\nbar\tbaz'
foo
bar baz

そのようなコマンドにエスケープ文字を渡したいときは、BashANSI-C Quotingという機能を使う。

https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html

www.gnu.org

### $'string' という形式で文字列を渡す
$ echo $'foo\nbar\tbaz'
foo
bar baz

cronで30秒毎にスクリーンショットを撮って、それを動画にするスクリプト

f:id:tatzyr:20161009115740g:plain

cronで30秒毎にスクリーンショットを撮って、それを動画にするスクリプトを作った。今のところはmacOS専用。

https://github.com/Tatzyr/screencapture

github.com

セットアップ

まずは、GitHubからスクリプトをcloneする。

$ cd
$ git clone https://github.com/Tatzyr/screencapture

次に、cloneしたスクリプトをcrontabに登録する。これで30秒に1回capture.shが実行されるようになる。

$ crontab -e
* * * * * $HOME/screencapture/capture.sh & sleep 30; $HOME/screencapture/capture.sh

動作の確認

crontabにスクリプトを登録して1分程度待つと、さっきcloneしたフォルダ(~/screencapture/)以下にフォルダと画像ファイルができるはず。

### 日付のフォルダができる
$ ls -altrF ~/screencapture/
20160909/   20160916/   20160923/   20160930/   20161007/
20160910/   20160917/   20160924/   20161001/   LICENSE
20160911/   20160918/   20160925/   20161002/   README.md
20160912/   20160919/   20160926/   20161003/   capture.sh*
20160913/   20160920/   20160927/   20161004/   encode.sh*
20160914/   20160921/   20160928/   20161005/
20160915/   20160922/   20160929/   20161006/

### 日付のフォルダの中にはスクリーンショット画像ファイルができる
$ ls -F  ~/screencapture/20161007/
1475818860.png  1475818980.png  1475819100.png  1475819220.png  1475819340.png
1475818890.png  1475819010.png  1475819130.png  1475819250.png
1475818920.png  1475819040.png  1475819160.png  1475819280.png
1475818950.png  1475819070.png  1475819190.png  1475819310.png

動画への変換

encode.shを使うと1日のスクリーンショット画像を動画に変換できる。

例えば、以下のコマンドを実行すると~/screencapture/20161007.m4vという8FPSの動画ファイルができる。

$ cd ~/screencapture
$ ./encode.sh 20161007

encode.shの中のVIDEO_WIDTHVIDEO_HEIGHTを変更すると、動画ファイルの大きさが変更できる。

Linuxbrewでsqliteを入れたのにgem install sqlite3が失敗した時の対処法

Linuxbrewsqliteを入れたあと、 $ gem install sqlite3を実行すると「sqlite3.hが見つからない」というエラーメッセージが出て失敗する。

$ gem install sqlite3
Building native extensions.  This could take a while...
ERROR:  Error installing sqlite3:
    ERROR: Failed to build gem native extension.

    current directory: /home/foo/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/sqlite3-1.3.11/ext/sqlite3
/home/foo/.rbenv/versions/2.3.1/bin/ruby -r ./siteconf20160801-29195-1vog9eq.rb extconf.rb
checking for sqlite3.h... no
sqlite3.h is missing. Try 'port install sqlite3 +universal',
'yum install sqlite-devel' or 'apt-get install libsqlite3-dev'
and check your shared library search path (the
location where your sqlite3 shared library is located).
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

そういう時は以下のようにLinuxbrewで入れたsqliteのパスを指定してあげればOK。

$ gem install sqlite3 -- --with-opt-dir=`brew --prefix sqlite`