1日はやくはじめた

2018/4/27 に休み入れて一日はやくGWはじめています。

毎月恒例

毎月月末金曜に知り合いの @n_kane と築地で朝御飯を食べにいってて 今月はGW直前だし朝からやってる 根津 鷹匠 で朝からそばもいこうとなっていってきた。 そういうわけで休みを入れていた。

国税局

前日あたりに instagramginzabrewery を眺めててどこだろと ggrks してみたら ここ の便利ページが出てきた。このページはだいたいブリュワリーとパブが一体化した 店が掲載されているよう。でよく行くCampion Ale や最近教えてもらった 十条すいけんブルワリー、 などが掲載されている。

ガハハハ

で午後は @n_kane も予定があったらしく、暇だったので上記国税局の便利ページを参考に ガハハビール に行ってみた。

流石に平日の午後3時だったので店員さん以外は誰もおらず、一人だけでした。 とりあえず Danchi IPA とレバーを頼んでみた。

Danchi IPA は苦味がすくなく美味しいビールでした。

つぎに Marcy IPA とライスなしカツカレーを注文。 こちらは苦味が強くおいしいビールでした。

そんなことより、謎のメニューが幾つかあり、店員さんに聞いてみたら、「だってお酒のむ人お米とか麺とかあんまりすきじゃないですよね」っていっておりました。 あと、軟骨バッファローチキンは頼んでないけどとてもよさそうだったので今度いく時に注文しよう。

おわり

国税局を調べたおかげで良いビール屋を見付けることができてよかったよかった。

ってガハハビールの情報探してたら老舗クラフトビールの紹介サイトの ビアクルーズ さんで 紹介されてた

NLP100 Knock Section IV

NLP100本ノック第4節おわりましたのでまとめます

第4章 形態素解析

形態素解析やからむずいやろとおもってた

準備

この章では、形態素解析済ファイルを作成する必要がありますが、毎回対象ファイルをダウンロード、解析して解いています。 ここではいつもどおり HashMap を利用するため ANALYZED_MECAB_KEYS を作成してこれをキーにします。 またよく利用する品詞を enum で定義しておき、変換関数 inspect を作成します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const ANALYZED_MECAB_KEYS: [&str; 9] = ["pos", "pos1", "pos2", "pos3", "a", "b", "base", "read", "speech"];
enum PartOfSpeech {
    VERB,
    NOUN,
    PARTICLE,
}

use PartOfSpeech::*;

fn inspect(val: PartOfSpeech) -> String {
    match val {
        VERB => "動詞",
        NOUN => "名詞",
        PARTICLE => "助詞",
    }.to_string()
}

30. 形態素解析結果の読み込み

Map を使えと指定があるので素直に利用します

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
fn feature(node: &Node) -> HashMap<String, String> {
    let mut h: HashMap<String, String> = HashMap::new();
    let surface: String = (&(node.surface)[..node.length as usize]).to_string();
    h.insert("surface".to_string(), surface);
    let values: Vec<String> = node.feature.split(",").map(|m| m.to_string()).collect();
    for (a, b) in ANALYZED_MECAB_KEYS.iter().zip(values.iter()) {
        h.insert(a.to_string(), b.to_string());
    }
    h
}

fn main() {
    let url = "http://www.cl.ecei.tohoku.ac.jp/nlp100/data/neko.txt".to_string();
    let neco: Vec<String> = NLP100::get(url).split("\n").filter(|f| f.ne(&"")).map(|m| m.to_string()).collect();
    let mut morph = Vec::new();

    for line in neco {
        let mut tagger: Tagger = mecab::Tagger::new("");
        let nodes: Node = tagger.parse_to_node(line);

        let mut mecabu: Vec<HashMap<String, String>> = Vec::new();
        for node in nodes.iter_next() {
            match node.stat as i32 {
                mecab::MECAB_BOS_NODE => (),
                mecab::MECAB_EOS_NODE => (),
                _ => {
                    mecabu.push(feature(&node));
                }
            }
        }
        morph.push(mecabu);
    }
    for morph in morphs {
        for mecab in morph {
            println!("{}", format!("surface: {}, base: {}, pos: {}, pos1: {}", mecab["surface"], mecab["base"], mecab["pos"], mecab["pos1"]));
        }
        println!("");
    }
}

31. 動詞

動詞だけ抽出するので以下のコードで抽出し、表層形("surface")を取得する

1
2
3
fn verb(nodes: Vec<HashMap<String, String>>) -> Vec<HashMap<String, String>> {
    nodes.iter().filter(|m| m["pos"] == inspect(VERB)).map(|hm| hm.clone()).collect()
}

32. 動詞の原形

動詞だけ抽出するので上記のコードで抽出し、原形("base")を取得する

33. サ変名詞

サ変接続を抽出

1
2
3
fn sa_noun(nodes: Vec<HashMap<String, String>>) -> Vec<HashMap<String, String>>{
    noun(nodes).iter().filter(|node| node["pos1"] == "サ変接続").map(|hm| hm.clone()).collect()
}

34. 「AのB」

「の」を挾んでいる名詞を抽出

1
2
3
4
5
6
7
8
9
10
11
fn between_noun(node: &Node) -> Option<String> {
    let mecab = feature(node);
    if mecab["surface"] == "の" && mecab["pos"] == inspect(PARTICLE) && mecab["pos1"] == "連体化" {
        let prev = feature(&node.prev().unwrap());
        let next = feature(&node.next().unwrap());

        Some(format!("{}{}{}", &prev["surface"], &mecab["surface"], &next["surface"]))
    } else {
        None
    }
}

35. 名詞の連接

連続した名詞を抽出するが、mecab でうまく関数化できなかったので割愛(あとでうかんだら追記)します。

36. 単語の出現頻度

単純に頻度をまとめ、 sort するとよい

1
2
3
4
5
6
7
8
fn word_histgram(nodes: Vec<HashMap<String, String>>) -> HashMap<String, u64> {
    let mut results: HashMap<String, u64> = HashMap::new();
    for node in nodes {
        let base = &node["base"];
        *results.entry(base.to_string()).or_insert(0) += 1;
    }
    results
}

37. 頻度上位10語

上記の結果より .take(10) するだけです。

38. ヒストグラム

37 と違いがわからずおわり。

39. Zipfの法則

単純に両対数グラフ化でおわり。

おわり

おわり

NLP 100 Knock Section 3

正規表現苦手なようでだいぶ時間がかかりました。

第3章 正規表現

正規表現ときいて楽勝やろと思ってた時期もあったんですが・・・

準備

この章では、gzファイルに入ったJSONをパースする必要があるのでさきにgzファイルから読み込むようにします。

1
2
3
4
5
6
fn read_gzip(path: String) -> Vec<String> {
    let mut file = File::open(path).unwrap();
    let mut string = String::new();
    Decoder::new(&mut file).unwrap().read_to_string(&mut string).unwrap();
    string.split("\n").filter(|m| m.ne(&"")).map(|m| m.to_string()).collect::<Vec<String>>()
}

20. JSONデータの読み込み

こいつは、serdeserde_json を利用して from_str で読み込むだけです。

1
2
3
4
let v: Value = match serde_json::from_str(&l.as_str()) {
     Ok(v) => v,
     Err(_) => { continue; },
};

21. カテゴリ名を含む行を抽出 && 22. カテゴリ名の抽出

早見表 を参考に Regex::captures で抽出することで解決。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;

extern crate regex;
extern crate serde_json;

use serde_json::Value;
use regex::Regex;

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    let path = &args[0].to_string();
    let keywords = &args[1..];
    let file = File::open(path).unwrap();
    let lines = BufReader::new(file).lines();
    let re = Regex::new(r"\[\[Category:.+\]\]").unwrap();
    let cap = Regex::new(r"\[\[Category:(?P<name>.+?)(?:\|.*)*\]\]").unwrap();

    for l in lines {
        let v: Value = serde_json::from_str(&l.unwrap()).unwrap();

        let title = &v["title"].as_str().unwrap().to_string();
        if keywords.contains(title) {
            for content in v["text"].as_str().unwrap().to_string().split("\n").filter(|m| re.is_match(m)) {
                for c in cap.captures_iter(content) {
                    println!("{}", &c["name"]);
                }
            }
        }
    }
}

23. セクション構造

こちらは前節の問題と同様に解けばよいですが、=の数 - 1 という罠があります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;

extern crate regex;
extern crate serde_json;

use serde_json::Value;
use regex::Regex;

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    let path = &args[0].to_string();
    let keywords = &args[1..];
    let file = File::open(path).unwrap();
    let lines = BufReader::new(file).lines();
    let re = Regex::new(r"(?P<level>==+)(?P<headline>.+?)(?:=+)").unwrap();

    for l in lines {
        let v: Value = serde_json::from_str(&l.unwrap()).unwrap();

        let title = &v["title"].as_str().unwrap().to_string();
        if keywords.contains(title) {
            for content in v["text"].as_str().unwrap().to_string().split("\n").filter(|m| re.is_match(m)) {
                for caps in re.captures_iter(content) {
                    println!("level: {}, headline: {}", &caps["level"].len() - 1, &caps["headline"]);
                }
            }
        }
    }
}

24. ファイル参照の抽出

このあたりは問題ないとおもいます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;

extern crate regex;
extern crate serde_json;

use serde_json::Value;
use regex::Regex;

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    let path = &args[0].to_string();
    let keywords = &args[1..];
    let file = File::open(path).unwrap();
    let lines = BufReader::new(file).lines();
    let re = Regex::new(r"\[\[(File|ファイル):(?P<filename>.+?)(?:\|.*)*(?:\|.*)*\]\]").unwrap();

    for l in lines {
        let v: Value = serde_json::from_str(&l.unwrap()).unwrap();

        let title = &v["title"].as_str().unwrap().to_string();
        if keywords.contains(title) {
            for content in v["text"].as_str().unwrap().to_string().split("\n").filter(|m| re.is_match(m)) {
                for caps in re.captures_iter(content) {
                    println!("filename: {}", &caps["filename"]);
                }
            }
        }
    }
}

25. テンプレートの抽出

こいつは大変でしたので参考サイト1 を参考に以下の正規表現でとりあえず抽出しています。

1
let re = Regex::new(r"(?ms)(?:^\{\{基礎情報.*?$)(?P<dict>.+?)(?:^\}\}$)").unwrap();

26. 強調マークアップの除去

こちらも同様に参考サイト2を参考に以下の正規表現で

1
let strong = Regex::new(r"'{2,5}").unwrap();

27. 内部リンクの除去

こちらも同様に参考サイト3を参考に以下の正規表現で

1
let link = Regex::new(r"(?:\[{1,2})(?P<link>.+?)(?:\|.+)?(?:\]{1,2})").unwrap();

28. MediaWikiマークアップの除去

どうよう4 (ry

1
2
3
let link = Regex::new(r"(?:\[{1,2})(?P<link>.+?)(?:\|.+)?(?:\]{1,2})").unwrap();
let lang = Regex::new(r"(?:\{\{lang\|.+?\|)(?P<lang>.+?)(?:\}\})").unwrap();
let markup = Regex::new(r"</*.+?>").unwrap();

29. 国旗画像のURLを取得する

こちらもさんこおおう5にしてますが、主にURL部分だけです。 こちらは Hyper を利用することで解決しています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
extern crate futures;
extern crate hyper;
extern crate hyper_tls;
extern crate libflate;
extern crate regex;
extern crate serde_json;
extern crate tokio_core;

use futures::{Future, Stream};
use hyper::Client;
use hyper_tls::HttpsConnector;
use libflate::gzip::Decoder;
use regex::Regex;
use serde_json::Value;
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::Read;
use tokio_core::reactor::Core;

fn get_image(query: String) -> String {
    let re = Regex::new(r" ").unwrap();
    let uri = format!("https://en.wikipedia.org/w/api.php?action=query&titles=File:{}&format=json&prop=imageinfo&iiprop=url", re.replace_all(&query, "%20"));
    let uri = uri.parse().unwrap();
    let mut core = Core::new().unwrap();
    let handle = core.handle();
    let client = Client::configure()
        .connector(HttpsConnector::new(4, &handle).unwrap())
        .build(&handle);
    let work = client.get(uri).and_then(|res| {
        res.body().concat2().and_then(move |body| {
            let v: Value = serde_json::from_slice(&body).unwrap();
            let page_ids = v["query"]["pages"].as_object().unwrap().keys().map(|m| m.to_string()).collect::<Vec<String>>();
            let imageinfo = &v["query"]["pages"][&page_ids[0]]["imageinfo"];
            let r = format!("{}", imageinfo[0]["url"].as_str().unwrap());
            Ok(r)
        })
    });
    core.run(work).unwrap()
}

fn read_gzip(path: String) -> Vec<String> {
    let mut file = File::open(path).unwrap();
    let mut string = String::new();
    Decoder::new(&mut file).unwrap().read_to_string(&mut string).unwrap();
    string.split("\n").filter(|m| m.ne(&"")).map(|m| m.to_string()).collect::<Vec<String>>()
}

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    let path = &args[0].to_string();
    let keywords = &args[1..];
    let lines = read_gzip(path.to_string());
    let re = Regex::new(r"(?ms)(?:^\{\{基礎情報.*?$)(?P<dict>.+?)(?:^\}\}$)").unwrap();
    let bar_hat = Regex::new(r"(?ms)(?:^\|)").unwrap();
    let dict = Regex::new(r"(?P<key>.+?)\s*=\s*(?P<val>.+)").unwrap();
    let media = Regex::new(r"\[\[(File|ファイル):(?P<filename>.+?)(?:\|.*)*(?:\|.*)*\]\]").unwrap();
    let strong = Regex::new(r"'{2,5}").unwrap();
    let link = Regex::new(r"(?:\[{1,2})(?P<link>.+?)(?:\|.+)?(?:\]{1,2})").unwrap();
    let lang = Regex::new(r"(?:\{\{lang\|.+?\|)(?P<lang>.+?)(?:\}\})").unwrap();
    let markup = Regex::new(r"</*.+?>").unwrap();

    for l in lines {
        let v: Value = match serde_json::from_str(&l.as_str()) {
            Ok(v) => v,
            Err(_) => { continue; },
        };
        let text: String = match v["text"].as_str() {
            Some(x) => x.to_string(),
            None => { continue; },
        };
        let title: String = match v["title"].as_str() {
            Some(x) => x.to_string(),
            None => { continue; },
        };

        if keywords.contains(&title) {
            let mut results: HashMap<String, String> = HashMap::new();
            let re: String = match re.captures(&text) {
                Some(caps) => caps["dict"].to_string(),
                None => {
                    println!("cannot capture dict!!");
                    continue;
                },
            };
            for line in bar_hat.split(&re).filter(|f| f.ne(&"")).map(|m| m.to_string()) {
                let line = line.replace("\n", "");
                let dict = match dict.captures(&line) {
                    Some(x) => x,
                    None => {continue;},
                };

                let val = dict["val"].to_string();
                let key = dict["key"].to_string();
                let file: String = media.replace_all(&val, "$filename").trim().to_string();
                let strong: String = strong.replace_all(&file, "").trim().to_string();
                let link: String = link.replace_all(&strong, "$link").trim().to_string();
                let markup: String = markup.replace_all(&link, "").trim().to_string();
                let val: String = match key.as_ref() {
                    "国旗画像" => get_image(markup.to_string()),
                    _ => lang.replace_all(&markup, "$lang").trim().to_string(),
                };
                results.insert(key, val);
            }
            for (k, v) in results {
                println!("{}: {}", k, v);
            }
        }
    }
}

おわり

とりあえず3章まで終わらせましたので形態素解析へと進みたいとおもいます(思っているだけ)

参考資料


  1. 素人の言語処理100本ノック:25, segavvy, https://qiita.com/segavvy/items/e402ad0a5b0f52453d7f

  2. 素人の言語処理100本ノック:26, segavvy, https://qiita.com/segavvy/items/f6d0f3d6eee5acc33c58

  3. 素人の言語処理100本ノック:27, segavvy, https://qiita.com/segavvy/items/9a8137f045852bc299d6

  4. 素人の言語処理100本ノック:28, segavvy, https://qiita.com/segavvy/items/8c4567ec1124320d3354

  5. 素人の言語処理100本ノック:29, segavvy, https://qiita.com/segavvy/items/fc7257012d8a590185e5

NLP100ノック第2章

第2章を一通り終えたので書きます。

10. 行数のカウント

タイトルのままです。 Rust では std::str::Linescount() がありますので利用しておわりです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;

fn main() {
    let mut args = env::args();

    args.next();
    for path in args {
        let f = File::open(path).unwrap();
        let br = BufReader::new(f);

        println!("{}", br.lines().count());
    }
}

11. タブをスペースに置換

コチラも、std::string::String.replace() 利用するだけです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;

fn main() {
    let mut args = env::args();
    args.next();
    for path in args {
        let file = File::open(path).unwrap();
        let reader = BufReader::new(file);

        for line in reader.lines() {
            println!("{}", line.unwrap().replace("\t", " "));
        }
    }
}

12. 1列目をcol1.txtに,2列目をcol2.txtに保存

こちらは単純にファイルに書き込むのと、分割ができれば問題ないです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use std::env;
use std::fs::File;
use std::io::{BufReader, BufWriter, Write};
use std::io::prelude::*;

fn main() {
    let mut args = env::args();

    args.next();
    for path in args {
        let f = path.clone().replace(".txt", "");
        let s = path.clone().replace(".txt", "");
        let file = File::open(path).unwrap();
        let br = BufReader::new(file);
        let mut first_column = BufWriter::new(File::create(format!("{}_col1.txt", f)).unwrap());
        let mut second_column = BufWriter::new(File::create(format!("{}_col2.txt", s)).unwrap());

        for line in br.lines() {
            let words = line.unwrap().split("\t").map(|m| m.to_string()).collect::<Vec<String>>();
            first_column.write(format!("{}\n", words[0]).as_bytes()).unwrap();
            second_column.write(format!("{}\n", words[1]).as_bytes()).unwrap();
        }
    }
}

13. col1.txtとcol2.txtをマージ

こちらは以前利用した、zip があれば問題ないです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use std::env;
use std::fs::File;
use std::io::{BufReader, BufWriter, Write};
use std::io::prelude::*;

fn main() {
    let mut args = env::args();
    if args.len() < 2 { panic!("col1.txt col2.txt"); }

    args.next();

    let first = args.next().unwrap();
    let second = args.next().unwrap();

    let fr = BufReader::new(File::open(first).unwrap()).lines().map(|m| m.unwrap().to_string()).collect::<Vec<String>>();

    let sr = BufReader::new(File::open(second).unwrap()).lines().map(|m| m.unwrap().to_string()).collect::<Vec<String>>();
    let mut merge_file =
        BufWriter::new(File::create("merge.txt".to_string()).unwrap());

    for (x, y) in fr.iter().zip(&sr) {
        merge_file.write(format!("{}\t{}\n", x, y).as_bytes()).unwrap();
    }

}

14. 先頭からN行を出力

head コマンドですので std::iter::Iterator.take() を利用するだけです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;

fn main() {
    let args = env::args().skip(1).collect::<Vec<String>>();
    let file = File::open(&args[0]).unwrap();

    let br = BufReader::new(file).lines().take((&args[1]).to_string().parse::<usize>().unwrap());

    for line in br.map(|m| m.unwrap().to_string()).collect::<Vec<String>>() {
        println!("{}", line);
    }
}

15. 末尾のN行を出力

tail コマンドです。こちらは std::iter::Iterator.skip() を利用してやるだけです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;

fn main() {
    let args = env::args().skip(1).collect::<Vec<String>>();
    let file = File::open(&args[0]).unwrap();
    let takes = (&args[1]).to_string().parse::<usize>().unwrap();
    let br = BufReader::new(&file).lines();
    let skips = br.count() - takes;
    let file = File::open(&args[0]).unwrap();
    let lines = BufReader::new(&file).lines().skip(skips);

    for line in lines {
        println!("{}", line.unwrap());
    }
}

16. ファイルをN分割する

こちらの実装は素朴な実装とし、行数で分割しております。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::BufWriter;
use std::io::prelude::*;

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    let path = &args[0].to_string();
    let file = File::open(path).unwrap();
    let count: usize = BufReader::new(file).lines().count();
    let div: usize = (&args[1]).to_string().parse().unwrap();
    let file = File::open(path).unwrap();
    let mut br = BufReader::new(file).lines();

    let t = (count as f64/ div as f64).ceil() as usize;

    for x in 1 .. div + 1 {
        let l = br.by_ref();
        let file = File::create(format!("{}.txt",x)).unwrap();
        let mut bw = BufWriter::new(file);
        for y in l.take(t).map(|m| m.unwrap().to_string()).collect::<Vec<String>>() {
            let a = format!("{}\n", y);
            bw.write(a.as_bytes()).unwrap();
            println!("{}: {}", x, y);
        }
    }
}

17. 1列目の文字列の異なり

ファイル読込 + HashSet で実装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use std::collections::HashSet;
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;

extern crate regex;
use regex::Regex;

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    let path = &args[0].to_string();
    let file = File::open(path).unwrap();
    let re = Regex::new(r"\W+").unwrap();
    let hs = BufReader::new(file).lines().map(|m|{
        let l = m.unwrap().clone();
        re.split(&l).next().unwrap().to_string()
    }).collect::<HashSet<_>>();

    for s in hs {
        println!("{}", s);
    }

}

18. 各行を3コラム目の数値の降順にソート

こちら、実数の比較を行う必要があり、すこし めんどう でした。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    let path = &args[0].to_string();
    let file = File::open(path).unwrap();
    let mut val = BufReader::new(file).lines().map(|m| m.unwrap().split("\t").skip(2).next().unwrap().parse::<f64>().unwrap()).collect::<Vec<f64>>();

    val.sort_by(|a, b| a.partial_cmp(b).unwrap());
    for v in val { println!("{}", v); }
}

19. 各行の1コラム目の文字列の出現頻度を求め,出現頻度の高い順に並べる

18の問題を更にカウントできるように変更した。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;

enum Value {
    USIZE(usize),
    NONE(()),
}

fn main() {
    let args = env::args().skip(1).collect::<Vec<String>>();
    let file = File::open(&args[0]).unwrap();
    let mut words: HashMap<String, usize> = HashMap::new();
    for m in BufReader::new(&file).lines() {
        let w = m.unwrap().to_string().split("\t").next().unwrap().to_string();
        let v = match words.get(&w) {
            None => 1,
            Some(n) => n + 1,
        };
        words.insert(w, v);
    }

    let mut vars: Vec<(&String, &usize)> = words.iter().collect();
    vars.sort_by(|a, b| b.1.cmp(a.1));
    for (w, v) in vars {println!("{}: {}", w, v);}

}

おわり

この章は慣れてきたのか比較的楽に解けています。

Nlp 100 Section 1 Part 3

前回、言語処理100本ノック の04までやったので05からやります。

05. ngram

こいつはbi-gramを単語、文字二つを実装するひつようがあります

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
fn bigram(words: Vec<String>) -> Vec<String> {
    let mut bi: Vec<String> = Vec::new();
    let mut i = 0;

    loop {
        let w = i + 2;
        if w > words.len() { break; }
        bi.push(words[i..w].join(""));
        i += 1;
    }
    bi
}

fn main() {
    let words = "I am an NLPer".split(' ').map(|m| m.to_string()).collect::<Vec<String>>();
    println!("\n===word bi-gram");
    for word in bigram(words) {
        println!("{}", word);
    }

    let words = "I am an NLPer".chars().map(|m| m.to_string()).collect::<Vec<String>>();
    for word in bigram(words) {
        println!("\"{}\"", word);
    }

}

06. 集合

これは単純に HashSet を利用して、解決します。HashSet の差集合は difference を利用し、和集合は union を、積集合は intersection をそれぞれ利用します。また、特定の要素が含有していることを判定するには contains を利用して判定します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
use std::collections::HashSet;

fn bigram(words: Vec<String>) -> HashSet<String> {
    let mut bi: HashSet<String> = HashSet::new();
    let mut i = 0;

    loop {
        let w = i + 2;
        if w > words.len() { break; }
        bi.insert(words[i..w].join(""));
        i += 1;
    }
    bi
}

fn chars(s: String) -> Vec<String> {
    s.chars().map(|m| m.to_string()).collect::<Vec<String>>()
}

fn main() {
    let s1 = bigram(chars("paraparaparadise".to_string()));
    let s2 = bigram(chars("paragraph".to_string()));

    println!("===UNION===");
    for x in s1.union(&s2) {
        println!("{}", x);
    }

    println!("\n===DIFF===");
    println!("===s1 - s2===");
    for x in s1.difference(&s2) {
        println!("{}", x);
    }
    println!("===s2 - s1===");
    for x in s2.difference(&s1) {
        println!("{}", x);
    }

    println!("\n===intersection===");
    for x in s1.intersection(&s2) {
        println!("{}", x);
    }

    println!("\n===INCLUDE===");
    let se = "se";
    println!("s1: {}", s1.contains(se));
    println!("s2: {}", s2.contains(se));
}

07. テンプレートによる文生成

これは format! を使えば終りです。(問題意図ほんとこれなんか?)

1
2
3
4
5
6
7
8
fn string_template(x: i8, y: &str, z: f32) -> String {
    format!("{}時の{}は{}", x, y, z)
}

fn main() {
    let string = string_template(12, "気温", 22.5);
    println!("{}", string);
}

08. 暗号文

ASCII以外の判定と、小文字のASCIIが判れば簡単です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use std::ascii::AsciiExt;

fn cipher(src: &str) -> String {
    let chars = src.chars().collect::<Vec<char>>();
    let mut result: String = String::new();
    for c in chars {
        let s = if c.is_ascii() {
            let var: u8 = c as u8;
            match var {
                97 ... 122 => (219 - (var)) as char,
                _ => c,
            }
        } else {
            c
        };
        result.push(s);
    }
    result
}

fn main() {
    println!("{}", cipher("Today is fine."));
    println!("{}", cipher(&cipher("Today is fine.")));
}

09. Typoglycemia

こちらは、 Vecshuffle 的なものがないので、rand を呼び出して shuffle を使います。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
extern crate rand;
use rand::Rng;

fn words(src: &str) -> Vec<String> {
    let mut result: Vec<String> = Vec::new();

    for s in src.split(' ').collect::<Vec<&str>>() {
        let mut chars = s.chars().collect::<Vec<char>>();
        if chars.len() < 5 {
            result.push(s.to_string());
        } else {
            let last_index = chars.len() - 1;
            let first_char = chars[0];
            let last_char = chars[last_index];

            let rand_chars = &mut chars[1..last_index];
            shuffle(rand_chars);
            let mut rand_string = String::new();
            for c in rand_chars { rand_string.push(*c) }
            result.push(format!("{}{}{}", first_char, rand_string, last_char));
        }
    }
    result
}

fn shuffle(chars: &mut [char]) {
    rand::thread_rng().shuffle(chars);
}

fn main() {
    let paragraph = "I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind .";

    for w in words(paragraph) {
        println!("{}", w);
    }
}

おわり

ということで Rust で言語処理100本ノック1章をやってみました。 最近 Ruby しか書いていなかったので、新鮮で楽しいですね Rust

NLP100本ノック Section 1 Part 2

前回、言語処理100本ノック の01までやったので02からやっていきます。

extern crate nlp100;

ってやれるように Cargo を作成

1
cargo new nlp100

02. 「パトカー」+「タクシー」=「パタトクカシーー」

これはムズカシイので素直に zip を利用する

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fn concat(t: (Vec<char>, Vec<char>)) {
    let (f, s) = t;
    let mut r = String::new();

    for (x, y) in f.iter().zip(s.iter()) {
        r.push_str(&format!("{}{}", x, y));
    }
    println!("{}", r);
}

fn main() {
    let p: Vec<char> = String::from("パトカー").chars().collect();
    let t: Vec<char> = String::from("タクシー").chars().collect();
    let f = (p, t);
    concat(f);
}

03. 円周率

この問題は思い切り勘違いしてたので、「これのどこが円周率なの?」って思ってました。こいつは Regex を用いて単語毎に分解、単語毎に文字数数えて解決してます。

1
2
3
4
5
6
7
8
fn char_count_list(w: &str) -> Vec<usize> {
    Regex::new(r"\W+").unwrap().split(w).map(|m| m.len()).collect()
}

fn main() {
    let pi = char_count_list("Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics.");
    println!("{:?}", pi);
}

04. 元素記号

これは、英語版「水兵リーベー僕の船」ですので条件に合うときだけ1文字に変更します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use std::collections::HashMap;

fn main() {
    let atomic_words: Vec<&str> = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can.".split(' ').collect();
    let mut atomic_table = HashMap::new();
    for (i, a) in atomic_words.iter().enumerate() {
        let chars = a.chars().map(|v| v.to_string()).collect::<Vec<String>>();
        let r = match i {
            0 | 4...8 | 14...15 | 18 => chars[0].to_string(),
            _ => chars[0..2].join(""),
        };
        atomic_table.insert(r, i + 1);
    }

    for (k, v) in &atomic_table {
        println!("{}: {}", k, v);
    }
}

おわり

やっぱり難しいのを実感

Happy New Year and New Language

いまさらですが、あけましておめでとうございます。

happy new language

年開けから新しくプログラミング言語(Rust)始めました。(ってわけでもない)

雑に rust book 2nd edition を一通り読み終えたので、 言語処理100本ノック をやりはじめました。 とりあえず第1章が終ったので、メモとしてのこします。 基本的に Rust (に限らず)でやりたいことがとくにないので ちょうどよさそうな勉強 として自然言語処理を選択しています。

躓いたところ

躓いていないところがないです。 まあやっていくうちに以下二つは常に書いておくと楽になるかなと。

1
2
3
4
5
6
7
8
9
// 1文字毎
fn chars(string: &str) -> Vec<char> {
    string.chars().collect::<Vec<char>>()
}

// 1単語毎
fn words(sentence: &str) -> Vec<&str> {
    sentence.split(' ').collect::<Vec<&str>>()
}

00. 文字列の逆順

これは簡単で(でもなかった)、1文字ずつ分解して反対化したあと String にします。

1
2
3
fn reverse_string(string: &str) {
    println!("{}", string.chars().rev().collect::<String>());
}

01. 「パタトクカシーー」

これも簡単で、1文字ずつ分解して抜き出します。(絶対違う)

1
2
3
4
fn main() {
    let c = "パタトクカシーー".chars().collect::<Vec<char>>();
    println!("{}{}{}{}", c[0], c[2], c[4], c[6]);
}

おわり

とりあえず2問解いてみたけど、自然言語処理 && Rust 難しい。

今年いったビール屋さん 2017

今年行ってよかったビアバー、ビアパブをまとめてみようとおもいます。

何軒いった?

まず、今年何軒いったのか確認します。

  1. 浅草ビアホール D’s diner
  2. Titans
  3. ブラッセルズ
  4. アンテナアメリカ
  5. WIZ CRAFT BEER and FOOD
  6. Bon Fire
  7. 谷中ビアホール
  8. アボットチョイス 渋谷店
  9. グッド スリープ ベーカー
  10. Mikkeller Tokyo
  11. デビルクラフト 五反田店
  12. BrewDog Roppongi
  13. Craft Beer Bar iBREW
  14. カンピオンエール
  15. シェイク シャック 東京国際フォーラム店
  16. Watering Hole
  17. ザロイヤルスコッツマン
  18. Belg Aube
  19. びあマ & Beer-Ma BAR
  20. Tail’s Ale House 本郷店
  21. クラフト ワークス
  22. 地ビールハウス 蔵くら
  23. BEER DINING The Griffon
  24. SWANLAKE Pub Edo 田町店

24軒、大分いってますね・・・

どこがよかった?

だいたいよいですね。Mikkeller, BrewDog, Swanlakeなどの直営店、Watering Hole, 蔵くら, Devil Craftの有名店は説明不要だとおもうんでパスします

特に気に入ったお店

北千住のびあマと大塚のTitansがとくによく、 両店ともボトルビールが充実しています。

びあマのほうはボトルビール選べるお店とタップや角打ちができる複合タイプのお店です。 近くに大きな公園があり、ここでビールを買ってわいわいしながら公園で飲むのがよいかんじです。店のなかで飲むのもよいです。

Titans は持ち込みありのお店で、近くに商店街があり、そこに有名なたこ焼き屋のはち八があるのでここで買ってきて飲むのもいいですし、 鍋持ち込んで鍋するのもよいですね。

あと、いいかんじの雰囲気で住宅街にある店でGood Sleep Bakerが好きです。 ここは Tap 数が少ないですが、パンがとてもおいしいです。 おすすめのメニューとしてはバインミーがとくにおすすめです。

今年行ってない店

意外でポパイ、萬感今年行ってないようでびっくりしてます。

おわり

こうやってみると24軒(重複してるのは数えていない)とほぼ毎週行ってるようで、それはお金がなくなるよね 今年、広島、兵庫行きましたが、どちらも日本酒しか飲まなかったので来年は、東京(関東)以外でも行こうとおもってます。

IRubyはじめました

表題のとおり、 IRuby をはじめてみました

インストール

インストールは簡単で gem install iruby でいけますが、私の利用している環境では依存している czmq が古かったので手動でインストールして回避しています。

1
2
3
4
5
6
7
8
git clone https://github.com/zeromq/czmq.git
cd czmq
./autogen
./configure --prefix=${HOME}/.local
make -j4
make install
gem install iruby cztop
LD_LIBRARY_PATH=${HOME}/.local/lib iruby

でインストールと動作はできます。画像貼るのがメンドイので ipython 貼っておきます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "  <script>\n",
       "  requirejs.config({paths: { 'plotly': ['https://cdn.plot.ly/plotly-latest.min'] }})\n",
       "  </script>\n",
       "\n",
       "\n",
       "<div id=\"c34db0db-dc37-4ae3-a2b3-33ab99830b89\" style=\"height: 100%; width: 100%;\"></div>\n",
       "\n",
       "<script>\n",
       " require(['plotly'], function(Plotly) { \n",
       "Plotly.newPlot(\n",
       "  'c34db0db-dc37-4ae3-a2b3-33ab99830b89',\n",
       "  [{\"x\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],\"y\":[100,81,64,49,36,25,16,9,4,1,0,1,4,9,16,25,36,49,64,81,100],\"type\":null,\"mode\":\"\"}],\n",
       "  {\"width\":1000,\"height\":500},\n",
       "  {\"linkText\":\"Export to plot.ly\",\"showLink\":true}\n",
       ")\n",
       "\n",
       "window.addEventListener('resize', function() {\n",
       "  Plotly.Plots.resize(document.getElementById('c34db0db-dc37-4ae3-a2b3-33ab99830b89'))\n",
       "})\n",
       " }) \n",
       "</script>"
      ],
      "text/plain": [
       "#<Plotly::Offline::HTML:0x000056356f14be00 @id=\"c34db0db-dc37-4ae3-a2b3-33ab99830b89\", @data=[{:x=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], :y=>[100, 81, 64, 49, 36, 25, 16, 9, 4, 1, 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100], :type=>nil, :mode=>\"\"}], @layout={:width=>1000, :height=>500}, @config={:linkText=>\"Export to plot.ly\", :showLink=>true}, @embedded=true>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "#<CZTop::Socket::PUB:0x56356e7f3620 last_endpoint=\"tcp://127.0.0.1:45835\">"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "require 'daru'\n",
    "require 'daru/plotly'\n",
    "include Daru::Plotly::Methods\n",
    "\n",
    "dv = Daru::Vector.new((-10..10).map{|n| n ** 2 })\n",
    "plot(dv, x: \"x\", y: \"y\").show"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Ruby 2.4.2",
   "language": "ruby",
   "name": "ruby"
  },
  "language_info": {
   "file_extension": ".rb",
   "mimetype": "application/x-ruby",
   "name": "ruby",
   "version": "2.4.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}

ウイスキーアドベントカレンダー2日目

ウイスキー Advent Calendar 2017 の2日目の記事です。

ウィスキーはピートの香りが強いほうがすきです。

とりあえず蒸留所に行ってきた話を 以前、白州蒸留所山崎蒸留所 に行ったことがあるのでその話を。

白州蒸留所

白州へは5年くらい前に5人でわいわいしながら行ってきました。 ここでは、工場見学のツアーに参加しました。 また、テイスティングルームですこしウイスキーを試せて良いです。

山崎蒸留所

山崎は去年、京都へ行く機会があり、ウイスキーを買うために行きました。 ここでも工場見学可能だったのですが、事前に予約が必要であるため 今回はパスして、工場に併設されているウイスキーのテイスティングルーム で幾つかのウイスキーをたのしみ、ショップでウイスキーと梅酒を買って 鴨川へ

おわり

日本のウイスキーはあまり得意じゃないのですが、蒸留所に行くのは楽しいし 楽しみ方を教えてもらえるので近くに蒸留所がある方は一度行ってみるとよいとおもいます