会社の先輩から「これ使ったらいい感じにデータ取ってこれるよ」と渡されたものが、 dplyrとかstringrとかよくわからないパッケージをガンガン導入した自由奔放なRのコードで困っています。 R言語自体にも慣れていないため、 「コードを使って何を表現したいのか」と「そのRのパッケージで何をできるのか(またどういう使いどころなのか)」を両方読み解かなければならず、けっこう大変です。
そのコードの中で「いくつか関数の入ったリストを高階関数に渡してfilterかける」ような操作をしていてしっかり読み解いてみたいのですが、 そのコードで取ってきたデータを使いたいだけなので、どうしても後回しにしてしまいます。 というわけで、Rの勉強は家でやることにしました。
「言語処理100本ノック」として、ちょうど勉強したいライブラリ(dplyrなど)をいい感じに使ってるRのコードがあったので、 これを題材にしようと思います。 こちらのリンク先にRのコードがあります。
東北大学の研究室が例題集のようです。 Pythonで同じ例題を解いた後、そのライブラリが何をやるものなのか考えてみます。
ちなみにPythonのバージョンは3.4.3です。
00. 文字列の逆順
TASK_STRING_00 = "stressed" print(TASK_STRING_00[::-1])
Pythonは文字列もリストっぽく扱え、リストと同様にスライスを使うことができます。
01. 「パタトクカシーー」
from functools import reduce TASK_STRING_01 = "パタトクカシーー" TASK_INDEX_01 = [0, 2, 4, 6] # パイプ(%>%)前までは文字列のリストを作っているらしい before_pipe = [TASK_STRING_01[x] for x in TASK_INDEX_01] # リスト内の文字を結合 print(reduce(lambda x,y: x+y, before_pipe))
02. 「パトカー」+「タクシー」=「パタトクカシーー」
自分はこんなふうに解きました。
from functools import reduce TASK_VEC_02 = ["パトカー", "タクシー"] text_list = [x + y for i_x, x in enumerate(TASK_VEC_02[0]) for i_y, y in enumerate(TASK_VEC_02[1]) if i_x == i_y] # => ['パタ', 'トク', 'カシ', 'ーー'] reduce(lambda x,y: x+y, text_list)
元のRのコードだと、
[,1] [,2] [,3] [,4] [1,] "パ" "ト" "カ" "ー" [2,] "タ" "ク" "シ" "ー"
ってマトリックスをstringr::str_cに渡して実現してるので、stringr::str_cの挙動を深く知ってないと読めない気がします…。
03. 円周率
TASK_STRING_03 = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics." # ドットとコンマを削除 before_pipe = re.sub(r'[,\.]', '', TASK_STRING_03) # スペースで区切って各単語の長さをとる print([len(x) for x in before_pipe.split(' ')])
04. 元素記号
import re TASK_INDEX_04 = [0, 4, 5, 6, 7, 8, 14, 15, 18] TASK_STRING_04 = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can." # ドットとコンマを削除する(必要無いけど) txt_remove_dots = re.sub(r'[,\.]', '', TASK_STRING_04) # インデックスによって先頭の1文字か2文字を返す関数 def get_head_string(string, index): if index in TASK_INDEX_04: return string[0] else: return string[:2] # リスト内包表記で連想配列に適用 answer = {i: get_head_string(x, i) for i, x in enumerate(txt_remove_dots.split(' '))} print(answer)
05. n-gram
import re # 文字bi-gramを得る関数 def char_bi_gram(string): # 空白等を削除 string_sub = re.sub(r'[,\. ]', '', string) return [[x , y] for i_x, x in enumerate(string_sub) for i_y, y in enumerate(string_sub) if i_x == i_y - 1] print(char_bi_gram('I am an NLPer')) # => [['I', 'a'], ['a', 'm'], ['m', 'a'], ['a', 'n'], ['n', 'N'], ['N', 'L'], ['L', 'P'], ['P', 'e'], ['e', 'r']] # 単語bi-gramを得る関数 def str_bi_gram(string): # 空白で区切る string_split = string.split(' ') return [[x , y] for i_x, x in enumerate(string_split) for i_y, y in enumerate(string_split) if i_x == i_y - 1] print(str_bi_gram('I am an NLPer')) # => [['I', 'am'], ['am', 'an'], ['an', 'NLPer']]
06. 集合
Pythonはset型があるからチートだと思う…。
from functools import reduce TASK_STRING_06_X = "paraparaparadise" TASK_STRING_06_Y = "paragraph" # 文字bi-gramを得る関数(さっき定義したやつ) def char_bi_gram(string): # 空白等を削除 string_sub = re.sub(r'[,\. ]', '', string) return [[x , y] for i_x, x in enumerate(string_sub) for i_y, y in enumerate(string_sub) if i_x == i_y - 1] # 文字bi-gramを得る関数の戻り値を['a', 'b'] => 'ab'に変換 def make_bigram_set(string): return set(reduce(lambda x,y: x+y, str_list) for str_list in char_bi_gram(string)) X = make_bigram_set(TASK_STRING_06_X) Y = make_bigram_set(TASK_STRING_06_Y) # 和集合 print(X | Y) # => {'ap', 'ag', 'is', 'ph', 'gr', 'ad', 'ra', 'pa', 'di', 'se', 'ar'} # 積集合 print(X & Y) # => {'ra', 'pa', 'ap', 'ar'} # 差集合 print(set(filter(lambda x: not x in Y, X))) # => {'di', 'se', 'ad', 'is'} # XとYそれぞれに"se"が含まれるかどうか print('se' in X) # => True print('se' in Y) # => False
07. テンプレートによる文生成
def displayTimeMessage(x, y, z): return "%d時の%sは%s" %(int(x), str(y), str(z)) x = 12 y = "気温" z = 22.4 print(displayTimeMessage(x,y,z)) # => 12時の気温は22.4
08. 暗号文
import re def cipher(string): def cipher_char(chara): if re.match(r'[a-z]', chara): return chr(219-ord(chara)) else: return chara return reduce(lambda x,y: x+y, map(cipher_char, string)) target_str_08 = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics." print(cipher(target_str_08)) # => Nld I mvvw z wirmp, zoxlslorx lu xlfihv, zugvi gsv svzeb ovxgfivh rmeloermt jfzmgfn nvxszmrxh.
09. Typoglycemia
import random from functools import reduce # 乱数種の指定 random.seed(1) EX_STRING_09 = "I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind ." def shuffle_word(word): # 長さが4以下の単語は並び替えない if len(word) <= 4: return word # 文字列を並べ替える(破壊的操作) word_list = list(word) random.shuffle(word_list) return reduce(lambda x,y: x+y, word_list) print(' '.join(map(shuffle_word, EX_STRING_09.split()))) # => I l'ontcdu lebeevi that I udolc luaaclty radtnsendu what I was nragdei : the olhpeneanm erpwo of the uhman mind .
TODO::
先輩、Pythonのほうが楽じゃないっすか…?
後半Rのコードのほうをちゃんと終えてないので、もう一回読んでみます。
Pythonで書いたコードでいうと、こっちのコードのほうがイケてる。 qiita.com
差集合って X - Y で出せるんだ。