Go言語でPythonのcollections.Counterを簡単に実装してみた
Pythonのcollections.Counter
は個人的にすごく好きなんですが,Go言語の練習を兼ねてGo言語で実装してみました.
完璧な実装ではなく,カウントできるのは文字列だけです.また,普通に実装間違えてる可能性もあります.自己責任でよろしくお願いします.
Pythonのcollections.Counter
についてはここでも紹介しています.細かい仕様については公式ページ参照のこと.
以下コードです.
package main import ( "fmt" "sort" ) type Counter struct { innerMap map[string]int sum int } type KeyCountPair struct { key string count int } type KeyCountPairList []KeyCountPair func (kcpl KeyCountPairList) Len() int { return len(kcpl) } func (kcpl KeyCountPairList) Swap(i, j int) { kcpl[i], kcpl[j] = kcpl[j], kcpl[i] } func (kcpl KeyCountPairList) Less(i, j int) bool { return kcpl[i].count > kcpl[j].count } func (c *Counter) MostCommon(n int) KeyCountPairList { length := len(c.innerMap) pairs := make(KeyCountPairList, length) idx := 0 for k, v := range c.innerMap { pairs[idx] = KeyCountPair{ key: k, count: v, } idx++ } sort.Sort(pairs) return pairs[:n] } func (c *Counter) Elements() []string { if c.sum == 0 { for _, v := range c.innerMap { if v > 0 { c.sum += v } } } elements := make([]string, c.sum) idx := 0 for k, v := range c.innerMap { if v > 0 { for i := 0; i < v; i++ { elements[idx] = k idx++ } } } return elements } func (c *Counter) Subtract(tc *Counter) *Counter { for k := range c.innerMap { c.AddN(k, -tc.Count(k)) } return c } func (c *Counter) Add(s string) { c.innerMap[s] += 1 c.sum += 1 } func (c *Counter) AddN(s string, n int) { c.innerMap[s] += n c.sum += n } func (c *Counter) Count(s string) int { return c.innerMap[s] } func NewCounter() *Counter { m := make(map[string]int) return &Counter{ innerMap: m, sum: 0, } } func main() { c := NewCounter() c.AddN("test", 3) c.AddN("apolo", 4) c.AddN("google", 8) c.AddN("microsoft", 3) tc := NewCounter() tc.Add("test") fmt.Println(c) // ==> &{map[microsoft:3 test:3 apolo:4 google:8] 18} fmt.Println(c.Count("word")) // ==> 0 fmt.Println(c.Subtract(tc)) // ==> &{map[test:2 apolo:4 google:8 microsoft:3] 17} fmt.Println(c.Elements()) // ==> [test test apolo apolo apolo apolo google google google google google google google google microsoft microsoft microsoft] fmt.Println(c.MostCommon(2)) // ==> [{google 8} {apolo 4}] }
sort.Sort
で降順にソートする場合ってLess
の不等号逆にするしかないってまじですか?
Pythonの文字列連結に"+"演算子は必要ない
Pythonの入門サイトとかだと,文字列連結に+
演算子を使うことがよく例示されているようです.
s = "this " + "is " + "test" print(s) ==> "this is test"
ですが,Pythonにおいて文字列連結には+
演算子は必要ないです.
s = "this " "is " "test" print(s) ==> "this is test"
「だからどうした演算子書かないほうがわかりにくいじゃねーかバーカバーカ」って感じですが,例えばPythonの標準ライブラリだとエラー文出力に使われています.
# [ここ](https://github.com/python/cpython/blob/master/Lib/collections/__init__.py#L64-66)より引用 if not args: raise TypeError("descriptor '__init__' of 'OrderedDict' object " "needs an argument")
要するに,長い文章を打ちたいけどトリプルクオートで書くとインデントが崩れて嫌で演算子も書きたくないみたいなときには使えそうですね(どんなときかは知りませんが)
情報セキュリティスペシャリストに受かった
情報セキュリティスペシャリストに受かった
表題の通りです.大学院生の間に高度試験に一つは受かっておきたかったので目標が叶いました.
行った対策などを残しておきたいと思います.
情報セキュリティスペシャリストとは?
独立行政法人であるIPAが行っている情報処理技術者試験の一つです.
情報処理技術者試験試験には4つのレベルがあり,その内の最高レベルの一つが
情報セキュリティスペシャリストです.
情報システムや組織に対する脅威や脆弱性を評価し、技術面・管理面での有効な対策を遂行できる セキュリティエンジニアや情報システム管理者を目指す方に最適です。 情報セキュリティの重要性はますます高まっており、いま最も旬なエンジニアです。
上記ページより引用してみました.まあ大仰なことは書いてありますが,試験の中身を見てみると
高度に技術的というよりは如何に人間という最悪の脆弱性と戦うか,といった試験のように思います.
まあ実際の仕事でもそうなんでしょうね・・・
個別のセキュリティ問題に関してというよりは,どこに脆弱性が起こり得るか,ということを
考える知識はついたように思うので,セキュリティの初歩を学ぶ上ではとてもいい試験だと思います.
自分も,ここからさらに知識を広げていきたいですね.(更新制になるという噂もありますし・・・)
点数
点数は正直よくありませんでした・・・特に午後II.まさかの及第点ギリギリ.
得点 | |
---|---|
午前I得点 | 78.20点 |
午前II得点 | 76.20点 |
午後I得点 | 78点 |
午後II得点 | 60点 |
それぞれ満点は100点,合格基準点は60点以上です.午後IIほんとにやばかった.
自己採点などはしていないので何が原因かはわかりませんが,読解力のなさが如実に出た気がします.
高得点を狙うならば,文章力が必須かと.
行った対策
買った参考書は以下の二つです.
二つともとても勉強の役に立ちました.特に下の過去問題集では,IPAの公式解答に異議を唱えていたりとても面白かったです.:)
一つ目を試験の始まる三ヶ月ほど前に購入し,一ヶ月ほど前までだらだらと勉強していました.
一週間前に過去問を始めて解いて30点ほどしか取れなかったので慌てて過去問題集を買ってきて
ひたすら過去問を解く→わからない問題・分野を復習するということを繰り返しました.
試験を受けてみて
上にも書きましたが,午後の試験は文章題です.問題文の文意を的確に汲み取る必要があります. また,記述の問題では高度な答えではなく,拍子抜けするくらい簡単な答えが正答である場合があるので, やはり慣れる意味を込めて過去問を解くのは必須でしょう.(「会社のラップトップを社外に持ち出すときに 気をつけるべきことはなんでしょう」→「肌身離さず持ち歩くこと」が答え!?)
次はデータベーススペシャリストを受けてみようと思っています.データベースの知識もわずかにしかないので また勉強が大変そう・・・
Pythonを始めた時から知っていたかったベターな書き方
かなり基本的な内容です.それと記事に一貫性があるわけではないです.
1. with statement
定番のwith
です.ほとんどの場合で使えると思うので,積極的に使いましょう.
Go
では同じようなことをdefer
を使ったりしますね.
# not good... f = open("./test.txt") foo = f.read()とかその辺 f.close()
# better! with open("./test.txt") as f: foo = f.read()
上のコードでは,例えばfoo = f.read()
の部分のコードが長い場合,f.close()
を忘れがちですよね.
下のコードではそのような心配が入りませんし,簡潔です.
2. collections
python
はよくbattery included
な言語と言われますが,その名の通り外部のライブラリに
あまり頼らず標準ライブラリだけで結構戦えます.
collections
も便利な標準ライブラリの一つです.collections
の中でも特にCounter
やdefaultdict
は
使う機会が個人的に多いです.
どちらもdict
型によく似た構造体ですが,dict
型にはない便利な機能が付いています.
- Counter
Counter
はその名の通り引数として渡されたiterable
なものの要素の数を数えてくれます.できたものはkey
に要素,value
に要素の数が入ります. またそれ以外にも,most_common(n)
というメソッドを使うと最頻出の要素から上位n
個の要素を要素をとって来てくれます.
from collections import Counter c = Counter([1,1,2,3,3,4]) print(c.most_common(2)) # ==> [(1, 2), (3, 2)]
同じことはdict
型にもできますが,よくやりがちなミスが下のような感じです.
d = {} for token in [1,1,2,3,3,4]: d[token] += 1 # ==> 当然エラー.初期値が割り当てられていない,
setdefault
などで頑張ってもいいですが,あまり美しくないですよね,
- defaultdict
defaultdict
もdict
型に似たようなクラスです.先ほどのダメなコードの例では初期値が割り当てられていなくて エラーとなっていましたが,dict
を使っていて最初から初期値を割り当てておいて欲しいということはよくあります. そんなときにこういう風に書くと便利です.
from collections import defaultdict d = defaultdict(list) for i, token in enumerate([1,1,2,3,3,4]): d[token].append(i) print(d) # ==> defaultdict(<class 'list'>, {1: [0, 1], 2: [2], 3: [3, 4], 4: [5]})
このように,定義するところで最初に空のlist
を初期値にすると示すだけで,新たなkey
でアクセスされた時に
空のlist
を最初から割り当てていてくれます.
3. sort
あるlist
やdict
をソートした上でイテレーションを回したいことはよくあると思います.list
はともかく,
dict
の場合はkey
でソートしたいときやvalue
でソートしたいとき,色々な場合があると思います.
d = {2: 3, 1: 4, 5: 1} for k, v in sorted(d.items()): print(k, v) # ==> 1 4, 2 3, 5 1の順番.keyで昇順の並び for k, v in sorted(d.items(), key=lambda x:x[1]): print(k, v) # ==> 5 1, 2 3, 1 4の順番.valueで昇順の並び
もっと複雑な場合,例えばPythonの公式ドキュメントでは
operator
モジュールを利用して以下のようなコードを紹介しています.
from operator import itemgetter, attrgetter class Student: def __init__(self, name, grade, age): self.name = name self.grade = grade self.age = age def __repr__(self): return repr((self.name, self.grade, self.age)) student_objects = [ Student('john', 'A', 15), Student('jane', 'B', 12), Student('dave', 'B', 10), ] print(sorted(student_tuples, key=itemgetter(2))) # ==> [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)] print(sorted(student_objects, key=attrgetter('age'))) # ==> [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)] # gradeでソートしてさらにageでソートする場合 print(sorted(student_tuples, key=itemgetter(1,2))) # ==> [('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)] print(sorted(student_objects, key=attrgetter('grade', 'age'))) # ==> 上と一緒
4. all(), any()
以前に少しだけ書いたことがありますが,any
とall
はかなり便利です.
例えば,以下のようなコードを書いている場合,allを使うとスッキリかけます.(ちょっと例えが極端ですが...)
# not good... cnt = 0 l = [1,1,2,3,4,4,5] for i in l: if i < 10: cnt += 1 if cnt == len(l): print("All numbers are less than 10")
# better! if all(x < 10 for x in l): print("All numbers are less than 10") if any(x >= 10 for x in l): print("All numbers are less than 10")
5. ファイルオブジェクトに対するイテレーション
Python
では,ファイルオブジェクトに対してイテレーションをかけられます.巨大なファイルを行ごとに読みたい
なんて時にすごく便利です.
with open("./test.txt") as f: for line in f: print(line)
公式ドキュメントのとおり細かい調整(シーケンス場所を変えるなど)はできませんが,多くの場合はこの書き方だけ覚えておけば十分でしょう.
5. sum
sum
関数はジェネレータも取ることができます.例えば以下のようなコードだと,わざわざlist
を生成するより早いです.
In [1]: %timeit sum(x for x in range(100)) 100000 loops, best of 3: 6.21 µs per loop In [2]: %timeit sum([x for x in range(100)]) 100000 loops, best of 3: 6.94 µs per loop
sum
以外にも結構こういう風に書けるのは多いので,遅いなーと思ったらジェネレータで書いてみるのもいいでしょう.
以上です.もっと紹介したいのですがめんどくなったので終わります. もし何か間違いやもっといい書き方などあれば教えてくれると嬉しいです.
Numpyで1-of-K表現を簡単に作る方法
愚直にやるとめんどくさそうでしたが、結構簡単に出来たのでメモ。 一応、1-of-K表現とは、ある要素だけが1で、それ以外が0なベクトルです。
[0, 0, 1, 0, 0]
みたいな奴。
ここでは、あるidx
の要素が1、それ以外が0であることがわかっていると仮定する。
import numpy as np K = 10 idx = 2 a = np.zeros(K) a.put(idx, 1) # ==> array([ 0., 0., 1., 0., 0., 0., 0., 0., 0., 0.])
簡単!