2018年02月27日

言語処理100本ノックでPython入門 #18 - リストのソート

言語処理100本ノック 2015の問題18に挑戦です。

■ 問題

hightemp.txtは,日本の最高気温の記録を「都道府県」「地点」「℃」「日」のタブ区切り形式で格納したファイルである.以下の処理を行うプログラムを作成し,hightemp.txtを入力ファイルとして実行せよ.さらに,同様の処理をUNIXコマンドでも実行し,プログラムの実行結果を確認せよ.

18. 各行を3コラム目の数値の降順にソート
各行を3コラム目の数値の逆順で整列せよ(注意: 各行の内容は変更せずに並び替えよ).確認にはsortコマンドを用いよ(この問題はコマンドで実行した時の結果と合わなくてもよい).

■ Pythonのコード
def sortBytemp(filename):
    with open(filename, 'r', encoding='utf8') as fin:
        lines = fin.readlines()
        #lines.sort(key=lambda x: float(x.split()[2]))
        #return lines
        return sorted(lines, key=lambda x: float(x.split()[2]))

def main(): lines = sortBytemp('hightemp.txt') for line in lines: print(line.rstrip())
if __name__ == '__main__': main()

■ リストの並び替え

いっぺんにメモリに読み込んでそれを並びかえることとします。 リストにsortメソッドが用意されています。調べてみると引数keyで比較キーを指定することができるみたいです。
lambdaキーワードを使って、以下のように書いてみたら、意図通りに並び替えてくれました。
lines.sort(key=lambda x: float(x.split()[2]))

C#風に書くと、
lines.OrderBy(s => double.Parse(s.split('\t')[2]));
みたいな感じですかね。

でも、このsortはリストそのものを並び替えてしまうんですね。
return lines.sort(key=lambda x: float(x.split()[2]))
って書いて、結果が表示されないので、あれっってなりました。

もし、元のリストの並びを保持したい場合には、
return sorted(lines, key=lambda x: float(x.split()[2]))
って書けば、新たなソート済みリストを生成してくれるみたいです。

■ 結果
千葉県	茂原	39.9	2013-08-11
埼玉県	鳩山	39.9	1997-07-05
大阪府	豊中	39.9	1994-08-08
山梨県	大月	39.9	1990-07-19
山形県	鶴岡	39.9	1978-08-03
愛知県	名古屋	39.9	1942-08-02
岐阜県	美濃	40	2007-08-16
群馬県	前橋	40	2001-07-24
山形県	酒田	40.1	1978-08-03
千葉県	牛久	40.2	2004-07-20
静岡県	佐久間	40.2	2001-07-24
愛媛県	宇和島	40.2	1927-07-22
群馬県	館林	40.3	2007-08-16
群馬県	上里見	40.3	1998-07-04
愛知県	愛西	40.3	1994-08-05
埼玉県	越谷	40.4	2007-08-16
山梨県	勝沼	40.5	2013-08-10
和歌山県	かつらぎ	40.6	1994-08-08
静岡県	天竜	40.6	1994-08-04
山梨県	甲府	40.7	2013-08-10
山形県	山形	40.8	1933-07-25
埼玉県	熊谷	40.9	2007-08-16
岐阜県	多治見	40.9	2007-08-16
高知県	江川崎	41	2013-08-12
  

Posted by gushwell at 22:35Comments(0)

2018年02月25日

言語処理100本ノックでPython入門 #17 - 集合

言語処理100本ノック 2015の問題17に挑戦です。

■ 問題
hightemp.txtは,日本の最高気温の記録を「都道府県」「地点」「℃」「日」のタブ区切り形式で格納したファイルである.以下の処理を行うプログラムを作成し,hightemp.txtを入力ファイルとして実行せよ.さらに,同様の処理をUNIXコマンドでも実行し,プログラムの実行結果を確認せよ.

17. 1列目の文字列の異なり
1列目の文字列の種類(異なる文字列の集合)を求めよ.確認にはsort, uniqコマンドを用いよ.

■ Pythonのコード
def gatherPrefecture(filename):
    collection = set([])
    with open(filename, 'r', encoding='utf8') as fin:
        for line in fin:
            pref = line.split()[0]
            collection.add(pref)
    return collection
def main(): prefs = gatherPrefecture('hightemp.txt') for name in prefs: print(name)
if __name__ == '__main__': main()


■ 今回のトピック

問題6でやったように、集合を扱うには、set()関数を使います。
s = set([1 ,2, 3])

このプログラムでは、
collection = set([])
で空のSetを作成しています。

addメソッドで要素を追加します。重複は勝手に除去してくれるので便利です。
collection.add(item)
今回は、それだけかな。
 
 
■ 結果
群馬県
埼玉県
和歌山県
高知県
岐阜県
千葉県
愛媛県
山梨県
大阪府
愛知県
山形県
静岡県
  
Posted by gushwell at 21:30Comments(0)

2018年02月22日

言語処理100本ノックでPython入門 #16 - リストをN分割

言語処理100本ノック 2015の問題16に挑戦です。

■問題


hightemp.txtは,日本の最高気温の記録を「都道府県」「地点」「℃」「日」のタブ区切り形式で格納したファイルである.以下の処理を行うプログラムを作成し,hightemp.txtを入力ファイルとして実行せよ.さらに,同様の処理をUNIXコマンドでも実行し,プログラムの実行結果を確認せよ.

16. ファイルをN分割する
自然数Nをコマンドライン引数などの手段で受け取り,入力のファイルを行単位でN分割せよ.同様の処理をsplitコマンドで実現せよ.


■ 最初に書いたPythonのコード
import sys
import os.path
def split(filename, n): with open(filename, 'r', encoding='utf8') as fin: lines = fin.readlines() total = len(lines) div = [(total + i) // n for i in range(n)] start = 0 for i in range(n): end = start + div[i] with open(getFileName(filename, i+1), 'w', encoding='utf8') as fout: fout.writelines(lines[start:end]) start = end
def getFileName(filename, n): name, ext = os.path.splitext(filename) return name + str(n) + ext
def main(): n = int(sys.argv[1]) split('hightemp.txt', n)
if __name__ == '__main__': main()

■ 本日のトピック等

演算子 //

除算をもとめる演算子ですが、結果は小数点以下は切り捨てられます。


total個をn個に分割する方法
div = [(total + i) // n for i in range(n)]
誰が考えたのか、とてもすっきりと書けますね。
ネットで調べていたら見つけました。 多少効率悪いけどそれを補って余りある簡潔さです。
例えば、tital = 24, n = 5 の場合は、divは`[4, 5, 5, 5, 5]`となります。


ファイルパスを拡張子とそれ以外に分割する
import os.path
name, ext = os.path.splitext(filename)
extには、ピリオドを含んだ文字列が返ります。
name + ext = filenameです。


ブロック内の変数のスコープ

with内で宣言?した変数は、withの外でも使えるみたいです。
というか、withに限らず、for文やif文でも同じようです。

この辺りは、JavaScriptのvar変数みたいですね。C#とは明らかに違っています。Pythonはかなり緩いですね。どちらがいいんだろう。


■次に書いたPythonのコード

もし、数十万行ある巨大なファイルだったら?
昔の人間なので、全部メモリに入れるのは気が引けます。ということで省メモリバージョンも作成しました。
2度読みしないといけないのだけど、まあ仕方がないです。
import sys
import os.path

def getFileName(filename, n): name, ext = os.path.splitext(filename) return name + str(n) + ext
def countLine(filename): count = 0 with open(filename, 'r', encoding='utf8') as fin: for _ in fin: count += 1 return count
def split(filename, n): total = countLine(filename) div = [(total + i) // n for i in range(n)] with open(filename, 'r', encoding='utf8') as fin: i = 0 for m in div: i += 1 with open(getFileName(filename, i), 'w', encoding='utf8') as fout: for _ in range(m): line = fin.readline() fout.write(line)
def main(): n = int(sys.argv[1]) split('hightemp.txt', n)
if __name__ == '__main__': main()

■ 本日のトピック等(2)

インクリメント演算子が無い
i++
って書いたら、エラーになりました
i += 1
は、大丈夫みたいです。


■ 結果

3分割した場合の結果です。

hightemp1.txt
高知県	江川崎	41	2013-08-12
埼玉県	熊谷	40.9	2007-08-16
岐阜県	多治見	40.9	2007-08-16
山形県	山形	40.8	1933-07-25
山梨県	甲府	40.7	2013-08-10
和歌山県	かつらぎ	40.6	1994-08-08
静岡県	天竜	40.6	1994-08-04
山梨県	勝沼	40.5	2013-08-10

hightemp2.txt
埼玉県	越谷	40.4	2007-08-16
群馬県	館林	40.3	2007-08-16
群馬県	上里見	40.3	1998-07-04
愛知県	愛西	40.3	1994-08-05
千葉県	牛久	40.2	2004-07-20
静岡県	佐久間	40.2	2001-07-24
愛媛県	宇和島	40.2	1927-07-22
山形県	酒田	40.1	1978-08-03

hightemp3.txt
岐阜県	美濃	40	2007-08-16
群馬県	前橋	40	2001-07-24
千葉県	茂原	39.9	2013-08-11
埼玉県	鳩山	39.9	1997-07-05
大阪府	豊中	39.9	1994-08-08
山梨県	大月	39.9	1990-07-19
山形県	鶴岡	39.9	1978-08-03
愛知県	名古屋	39.9	1942-08-02
  
Posted by gushwell at 22:00Comments(0)

2018年02月20日

言語処理100本ノックでPython入門 #15 - None

言語処理100本ノック 2015の問題15に挑戦です。

■ 問題

hightemp.txtは,日本の最高気温の記録を「都道府県」「地点」「℃」「日」のタブ区切り形式で格納したファイルである.以下の処理を行うプログラムを作成し,hightemp.txtを入力ファイルとして実行せよ.さらに,同様の処理をUNIXコマンドでも実行し,プログラムの実行結果を確認せよ.

15. 末尾のN行を出力
自然数Nをコマンドライン引数などの手段で受け取り,入力のうち末尾のN行だけを表示せよ.確認にはtailコマンドを用いよ.


■ Pythonのコード
import sys
def tail(fin, fout, count): lines = fin.readlines() fout.writelines(lines[-count:])
def main(): n = int(sys.argv[1]) with open('hightemp.txt', 'r', encoding='utf8') as f: tail(f, sys.stdout, n)
if __name__ == '__main__': main()
これまでの知識を使えば出来ますが、たぶん、上のコードは入力ファイルをすべてメモリにため込むことになるので、 メモリを圧迫するような巨大なテキストファイルだと、この実装は好ましくないです。

なのでN行分だけをリストにため込むコードも書いてみました。
def tail(fin, fout, count):
    buff = [None] * count
    i = 0
    for line in fin:
        buff[i] = line
        i = i + 1 if i + 1 < count else 0
    for _ in range(count):
        if buff[i] != None:
            fout.write(buff[i])
        i = i + 1 if i + 1 < count else 0

5行しかないテキストファイルに対して、10行を出力するような指定をされた場合にもうまく動作するようにしたので、 ちょっとコードがごちゃごちゃしてしまいました。
RingBufferのようなクラスがあれば、もっと簡単に書けるのだけど... まあ、良しとしよう。


■ 今回のトピック

サイズを指定してリストの確保

これは、そういった構文は用意されていないようです。 ただし、次のように書けば、5個分の要素を初期値指定で確保できます。
buff = [1] * 5
buffの値は、
[1, 1, 1, 1, 1]
となります。


None

Noneは値の非存在を示します。C#でいうところのnullと同様の目的で使えそう。
buff = [None] * count
で、リストを初期化しています。
それと、Noneの判定は、is または、is not を使うみたいです。なので、
if buff[i] != None:
ではなく、
if buff[i] is not None:
のほうが良いらしいです。

■ 結果
$ python nlp15.py 5

埼玉県  鳩山    39.9    1997-07-05
大阪府  豊中    39.9    1994-08-08
山梨県  大月    39.9    1990-07-19
山形県  鶴岡    39.9    1978-08-03
愛知県  名古屋  39.9    1942-08-02
  
Posted by gushwell at 22:20Comments(0)

2018年02月18日

言語処理100本ノックでPython入門 #14 - コマンドラインパラメータ、標準出力

言語処理100本ノック 2015の問題14に挑戦です。

■ 問題

hightemp.txtは,日本の最高気温の記録を「都道府県」「地点」「℃」「日」のタブ区切り形式で格納したファイルである.以下の処理を行うプログラムを作成し,hightemp.txtを入力ファイルとして実行せよ.さらに,同様の処理をUNIXコマンドでも実行し,プログラムの実行結果を確認せよ.

14. 先頭からN行を出力
自然数Nをコマンドライン引数などの手段で受け取り,入力のうち先頭のN行だけを表示せよ.確認にはheadコマンドを用いよ.


■ Pythonのコード
import sys
def head(fin, fout, count): for _ in range(count): line = fin.readline() fout.write(line)
def main(): n = int(sys.argv[1]) with open('hightemp.txt', 'r', encoding='utf8') as f: head(f, sys.stdout, n)
if __name__ == '__main__': main()

■ 今回のトピックなど

コマンドラインからパラメータを受け取る

1番目のパラメータは、sys.argv[1]。 sys.argv[0]は、自分自身のプログラムパスが入ります。


文字列を数値(integer)に変換
n = int("14")  
nには、int 14が代入されます。 これって、文法的にはintのコンストラクタ呼び出しなんだろうか?それともint関数なんだろうか?


標準出力 sys.stdout

printではなく、sys.stdoutを使い、標準出力に文字列を出力してみました。
sysをimportすれば利用できます。
import sys
f = sys.stdout f.write(line)
write(line)は、それ自身では改行しません。でもlineそのものに改行コードが入っているので、これでOKです。


標準入力 sys.stdin 

ちなみに、標準入力はsys.stdinを使います。 以下のように使います。
import sys
f = sys.stdin line = f.readline()
lineには改行コードが入っています。


ループ変数を省略

今回の例では、n回繰り返したいだけで、ループ変数は必要ありません。
前にも書いた気がするけど、そんなときには、_ を使えば良いです。


■ 結果

コマンドの引数に5を指定した場合の結果です。
$ python nlp14.py 5

高知県  江川崎  41      2013-08-12
埼玉県  熊谷    40.9    2007-08-16
岐阜県  多治見  40.9    2007-08-16
山形県  山形    40.8    1933-07-25
山梨県  甲府    40.7    2013-08-10
  
Posted by gushwell at 22:10Comments(0)

2018年02月15日

言語処理100本ノックでPython入門 #13 - zip関数

本日は、言語処理100本ノック 2015の問題13に挑戦です。

■ 問題

hightemp.txtは,日本の最高気温の記録を「都道府県」「地点」「℃」「日」のタブ区切り形式で格納したファイルである.以下の処理を行うプログラムを作成し,hightemp.txtを入力ファイルとして実行せよ.さらに,同様の処理をUNIXコマンドでも実行し,プログラムの実行結果を確認せよ.

13. col1.txtとcol2.txtをマージ
12で作ったcol1.txtとcol2.txtを結合し,元のファイルの1列目と2列目をタブ区切りで並べたテキストファイルを作成せよ.確認にはpasteコマンドを用いよ.


■ Pythonのコード
def solve(infile1, infile2, outfile):
    with open(outfile, 'w', encoding='utf8') as fw, \
         open(infile1, 'r', encoding='utf8') as fr1, \
         open(infile2, 'r', encoding='utf8') as fr2:
        for word1, word2 in zip(fr1, fr2):
            fw.write(word1.rstrip() + '\t' + word2)

def main():
    solve('col1.txt', 'col2.txt', 'connected.txt')
    print('end.')

if __name__ == '__main__':
    main()

■ zip関数

調べたら、zip関数がstreamにも使えることがわかりました。
streamに対してzip関数が使えるなんて、うーん、すごいですね。便利すぎます。 C#のLINQのZipメソッドも便利ですが、これに関してはPythonの勝ちですね。

行末の改行除去に、rstrip関数を使っています。C#やってるプログラマーには、改行が自動で除去されないのは本当に不便です。改行除去してくれるオプションを用意してくれていればいいのにと思います。

なお、問題にはファイル名の指定がないので、connected.txtとしました。 


■ Visual Studio CodeでPython環境を切り替える

ところで、PC内に複数のPython環境がある場合、VSCodeはどうやって、それを指定すればいいのかなと疑問だったのですが、ちゃんと公式サイトの以下のページに書いてありました。
https://code.visualstudio.com/docs/python/environments


Command Palette (Ctrl+Shift+P)で、「Python: Select Interpreter」を選べば、一覧が出てくるので、それで選択すればいいみたいです。






※ スクリーンショットは、公式サイトのものです。

anacondaの仮想環境もちゃんと認識してくれるようです。


■ 結果
高知県	江川崎
埼玉県	熊谷
岐阜県	多治見
山形県	山形
山梨県	甲府
和歌山県	かつらぎ
静岡県	天竜
山梨県	勝沼
埼玉県	越谷
群馬県	館林
群馬県	上里見
愛知県	愛西
千葉県	牛久
静岡県	佐久間
愛媛県	宇和島
山形県	酒田
岐阜県	美濃
群馬県	前橋
千葉県	茂原
埼玉県	鳩山
大阪府	豊中
山梨県	大月
山形県	鶴岡
愛知県	名古屋

  
Posted by gushwell at 22:30Comments(0)

2018年02月13日

言語処理100本ノックでPython入門 #12 - テキストファイルの出力

本日は、言語処理100本ノック 2015の問題12に挑戦です。

■ 問題

hightemp.txtは,日本の最高気温の記録を「都道府県」「地点」「℃」「日」のタブ区切り形式で格納したファイルである.以下の処理を行うプログラムを作成し,hightemp.txtを入力ファイルとして実行せよ.さらに,同様の処理をUNIXコマンドでも実行し,プログラムの実行結果を確認せよ

12. 1列目をcol1.txtに,2列目をcol2.txtに保存
各行の1列目だけを抜き出したものをcol1.txtに,2列目だけを抜き出したものをcol2.txtとしてファイルに保存せよ.確認にはcutコマンドを用いよ.


■ 最初に書いたPythonのコード
def solve(infile, outfile1, outfile2):
    col1 = []
    col2 = []
    with open(infile, 'r', encoding='utf8') as f:
        for line in f:
            words = line.split('\t')
            col1.append(words[0])
            col2.append(words[1])
    with open(outfile1, 'w', encoding='utf8') as f:
        for word in col1:
            f.write(word)
            f.write('\n')
    with open(outfile2, 'w', encoding='utf8') as f:
        f.writelines('\n'.join(col2))
        f.write('\n')

def main():
    solve1('hightemp.txt', 'col1.txt', 'col2.txt')

if __name__ == '__main__':
    main()


■ テキストファイルの出力

テキストファイルへの出力は、writeメソッドを使いますが、改行は付加してくれません。 writelineというメソッドはないので、以下のように書く必要があります。
with open(outfile1, 'w', encoding='utf8') as f:
    for word in col1:
        f.write(word)
        f.write('\n')

writelinesというメソッドもあるので、joinと組み合わせて、いっぺんに出力することもできます。 ただし、最後の行の改行が付加されないので、最後に改行コードを出力しています。
with open(outfile2, 'w', encoding='utf8') as f:
    f.writelines('\n'.join(col2))
    f.write('\n')


■ 複数のファイルを同時に開く

最初のコードは、いったん入力ファイルを閉じてから、col1.txt, col2.txtを出力しましたが、3つのファイルを一度に開く方法もあります。

with open(infile, 'r', encoding='utf8') as f, open(outfile1, 'w', encoding='utf8') as fw0, open(outfile2, 'w', encoding='utf8') as fw1:
    for line in f:
        words = line.split('\t')
        fw0.write(words[0] + '\n')
        fw1.write(words[1] + '\n')

でも1行が長すぎます。

バックスラッシュを使うと、複数行に分けられます。

with open(infile, 'r', encoding='utf8') as f, \
     open(outfile1, 'w', encoding='utf8') as fw0, \
     open(outfile2, 'w', encoding='utf8') as fw1:
    for line in f:
        words = line.split('\t')
        fw0.write(words[0] + '\n')
        fw1.write(words[1] + '\n')

このあたりが、僕の知っている言語と大きく違うところですね。


■ 最終版のPythonのコード

最終版のコードを示します。
def solve(infile, outfile1, outfile2):
    with open(infile, 'r', encoding='utf8') as f, \
         open(outfile1, 'w', encoding='utf8') as fw0, \
         open(outfile2, 'w', encoding='utf8') as fw1:
        for line in f:
            words = line.split('\t')
            fw0.write(words[0] + '\n')
            fw1.write(words[1] + '\n')
def main():
    solve('hightemp.txt', 'col1.txt', 'col2.txt')

if __name__ == '__main__':
    main()


■ 結果

col1.txt
高知県
埼玉県
岐阜県
山形県
山梨県
和歌山県
静岡県
山梨県
埼玉県
群馬県
群馬県
愛知県
千葉県
静岡県
愛媛県
山形県
岐阜県
群馬県
千葉県
埼玉県
大阪府
山梨県
山形県
愛知県

col2.txt
江川崎
熊谷
多治見
山形
甲府
かつらぎ
天竜
勝沼
越谷
館林
上里見
愛西
牛久
佐久間
宇和島
酒田
美濃
前橋
茂原
鳩山
豊中
大月
鶴岡
名古屋
  
Posted by gushwell at 21:12Comments(0)

2018年02月12日

言語処理100本ノックでPython入門 #11 - 文字列の置換

本日は、言語処理100本ノック 2015の問題11に挑戦です。

■ 問題
hightemp.txtは,日本の最高気温の記録を「都道府県」「地点」「℃」「日」のタブ区切り形式で格納したファイルである.以下の処理を行うプログラムを作成し,hightemp.txtを入力ファイルとして実行せよ.さらに,同様の処理をUNIXコマンドでも実行し,プログラムの実行結果を確認せよ.

11. タブをスペースに置換
タブ1文字につきスペース1文字に置換せよ.確認にはsedコマンド,trコマンド,もしくはexpandコマンドを用いよ.

■ Pythonのコード

import re

def tabToSpace(source):
    return re.sub(r'\t', ' ', source)


def main():
    with open('hightemp.txt', 'r', encoding='utf8') as f:
        for line in f:
            print(tabToSpace(line.rstrip()))

    with open('hightemp.txt', 'r', encoding='utf8') as f:
        print(tabToSpace(f.read()))

if __name__ == '__main__':
    main()


■ 正規表現


正規表現を使うには、
import re
と、importします。 タブを1文字空白に置き換えるには、subメソッドを使います。
def tabToSpace(source):
    return re.sub(r'\t', ' ', source)
Python の r'...' という raw string 記法を使うのが無難。C#の逐語的文字列リテラルと同じかな。

でも、この例に限れば、以下のように書けるので、そもそも正規表現使う必要もなかったですね。
return source.replace('\t', ' ' )
関数定義する必要もないですが、ここでは、tabToSpaceを定義してみました。 


■ テキストファイルの入力(readメソッド)

'hightemp.txt'を入力ファイルとして、以下のようなコードを書きました。
with open('hightemp.txt', 'r', encoding='utf8') as f:
    for line in f:
        print(tabToSpace(line.rstrip()))

rstripは、右側の空白を除去するメソッドです。

'hightemp.txt'そのものを書き換えよという問題ではないと思うので、コンソールに出力しています。

ファイルの内容を全部メモリに入れてしまって
with open('hightemp.txt', 'r', encoding='utf8') as f:
    print(tabToSpace(f.read()))
としても良いですね。
このほうが、改行コードを取り除くコードが不要になるのでコードが短くなります。hightemp.txt'ファイルがとても小さなものなので、このほうが良いかもです。
   
Posted by gushwell at 21:20Comments(0)