More Related Content Similar to Apache Spark チュートリアル (20) More from K Yamaguchi (7) Apache Spark チュートリアル4. 素朴な実装 3
frequency = defaultdict(int)
for line in opened_file:
for word in some_splitter(line):
frequency[word] += 1
for word in frequency:
some_output(word, frequency[word])
ファイル
frequency
メモリに
辞書/ハッシュ/
連想配列/Mapを
持つ頻度(Pythonic)
frequency = collections.Counter(
word
for line in opened_file
for word in some_splitter(line))
for word, count in frequency.iteritems():
some_output(word, count)
5. 巨大なファイルだと…… 4
:
for line in opened_file:
for word in some_splitter(line):
frequency[word] += 1
:
巨大な
ファイル メモリが
足りない!!
7. 出力を分割する 6
frequency = defaultdict(int)
for line in opend_file:
for word in some_splitter(line):
if hash(word) % 2 == 1:
frequency[word] += 1
:
frequency = defaultdict(int)
for line in opend_file:
for word in some_splitter(line):
if hash(word) % 2 == 0:
frequency[word] += 1
:
巨大な
ファイル
頻度
頻度
ハッシュの
剰余で間引いて
半分に
ある単語は一方の
ファイルにだけ存在
単純に結合すればOK
もう一度読む
残り半分
8. 組み合わせる 7
f = [defaultdict(int)
for i in range(2)]
for l in of:
for w in sp(l):
f[hash(w) % 2]
[w] += 1
ファイル
ファイル
同じ単語が
入っている
ファイル同士を
統合する
剰余で分割
ファイルを
分割
単純な
結合でOK
並列可能
11. でもHadoopは簡単じゃなかった
定型部分の方が多い
何度もMapReduce
する処理を書こうと
すると、
ほとんど同じで
微妙な違いしかない
ソースがたくさん
できる
10
/*
* 仮型引数
* FileInputFormatだと第1引数はLongWriteable, 第2引数はtext。
* 第3引数がmap出力のkey, 第4引数がmap出力のvalue
*/
public static class Map
extends Mapper<LongWritable, Text, Text, LongWritable> {
private Text outKey = new Text("LINES");
private LongWritable outValue = new LongWritable(1);
@Override
protected void map(LongWritable inputKey,
Text value,
Context context)
throws IOException, InterruptedException {
context.write(outKey, outValue);
}
}
/*
* <map出力のkey=reduce入力, map出力のvalue=reduce入力,
* reduce出力のkey, reduce出力のvalue>
*/
public static class Reduce
extends Reducer<Text, LongWritable, Text, LongWritable> {
private LongWritable outValue = new LongWritable();
@Override
protected void reduce(Text key,
Iterable<LongWritable> values,
Context context)
throws IOException, InterruptedException {
long total = 0L;
for (LongWritable value : values) {
total += value.get();
}
outValue.set(total);
context.write(key, outValue);
}
}
←行数をカウントする
MapReduce
個人の感想です
19. Sparkのコンポーネント 18
node00 node01 node02 node03
node04 node05 node20
driver
executor
executor
master
executor
executor
executor
executor
executor
executor
executor
executor
executor
(shell実行モードの場合)
20. driver
19
node00 node01 node02 node03
node04 node05 node20
driver
executor
executor
master
executor
executor
executor
executor
executor
executor
executor
executor
executor
利用者が見ているコンソール(spark-shell)
WebUIも提供する
定数・関数などの定義(定義したものは各
executorに分散され共有される)
21. executor
20
node00 node01 node02 node03
node04 node05 node20
driver
executor
executor
master
executor
executor
executor
executor
executor
executor
executor
executor
executor
実際にジョブを実行している部分
22. master
21
node00 node01 node02 node03
node04 node05 node20
driver
executor
executor
master
executor
executor
executor
executor
executor
executor
executor
executor
executor
SparkではなくHadoop YARNのコンポーネント
なにをしているかはわかってない
YARN Resource Managerとやりとりしている(?)
29. RDD "Resilient Distributed Dataset"
分散データを扱う抽象コレクション
データの実体がどこにあるのかを意識しない
並列動作する変換操作(map, filter,,)
キャッシュの選択肢
メモリにキャッシュ, ディスクにキャッシュ
28
30. RDD : 作成
RDDの作成はSparkContextから
29
(Python)
sc.parallelize([1, 2, 3, 4, 5])
# ローカルコレクションをRDDに変換
sc.textFile("file.text")
sc.textFile("directory/*.gz")
# 文字列のコレクションになる
# テキストファイルを指定してRDDを作成
# クラスタがローカル実行ならローカルのファイルから
# クラスタがHadoop-YARN上にあるならHDFSから
# クラスタがAWS上にあるならS3から読みこんだり
# 圧縮ファイルなら解凍後のデータを読む
# 実行してもすぐにファイルを読みこまない
Spark-Shellのメモリ上で
インスタンス化している
という意味
31. RDD : アクション(1) 30
(Python)
nums = sc.parallelize([4, 3, 2, 5, 1])
nums.collect()
# => [4, 3, 2, 5, 1]
# 全ての要素をローカルのコレクションにする
nums.take(3)
# => [4, 3, 2] 冒頭3個の要素
nums.top(3)
# => [5, 4, 3] 自然順序づけで大きい方から3つの要素
RDDから具体的なデータへ変換する
(ローカルコレクション, 値, ファイル)
Pythonだとcmp()関数
Java/ScalaだとComparable
での比較
32. RDD : アクション(2) 31
(Python)
nums = sc.parallelize(xrange(1000))
nums.count()
# => 1000
# 要素数
nums.reduce(lambda x, y: x + y)
# => 499500
# 畳み込み (((…(((0+1)+2)+3)+…+998)+999)
nums.saveAsTextFile("destination")
# テキストファイルとして出力
RDDから具体的なデータへ変換する
(ローカルコレクション, 値, ファイル)
33. RDD : 変換(基本)
RDDを元に別のRDDを定義する
RDDはイミュータブル(書き換え不能)
32
(Python)
nums = sc.parallelize([1, 2, 3])
nums.map(lambda x: x * x)
# => [1, 4, 9] 関数適用
nums.filter(lambda x: x % 2 == 0)
# => [2]フィルタリング
nums.flatMap(lambda x: range(x))
# => [0, 0, 1, 0, 1, 2]
# 「1つの値からコレクションを返す」関数を元に複数データを生成
34. RDD : 変換(2-value tuple)
2値タプルのRDDに追加されるメソッド
33
(Python)
pets = sc.parallelize(
[("cat", 1), ("dog", 1), ("cat", 2)])
pets.groupByKey()
# => [("cat", [1, 2]), ("dog", [1])]
pets.reduceByKey(lambda x, y: x + y)
# => [("cat", 3), ("dog", 1)]
pets.sortByKey()
# => [(cat, 1), (cat, 2), (dog, 1)]
35. RDD : 変換(2つの2-value tuple)
2値タプルのRDDに追加されるメソッド
34
(Python)
pets = sc.parallelize(
[("cat", 1), ("dog", 1), ("cat", 2)])
names = sc.parallelize(
[("cat", "Tama"), ("dog", "Pochi")])
pets.join(names)
# => [("dog", (1, "Pochi")),
# ("cat", (1, "Tama")),
# ("cat", (2, "Tama"))]
pets.cogroup(names)
# => [("dog", ([1], ["Pochi"])),
# ("cat", ([1,2], ["Tama"]))] 相当
36. RDD : メソッド群 35
アクション
データの具体化
変換
抽象コレクションの操作
foreach
collect
reduce
fold
count
saveAsTextFile
map
flatMap
filter
collect
distinct
sample
sortByKey
groupByKey
reduceByKey
zip
cartesian
union
intersection
subtract
repartition
join
cogroup
etc…
take
first
top
takeOrdered
etc…
メタ操作
cache
unpersist
checkpoint
etc…
こっちのcollectは
Pythonにはない
37. RDD : 資料
http://spark.apache.org/docs/latest/api/scala/index.html#or
g.apache.spark.rdd.RDD
http://spark.apache.org/docs/latest/api/python/pyspark.ht
ml#pyspark.RDD
http://www.ne.jp/asahi/hishidama/home/tech/scala/spark/R
DD.html
36
(mezcalのSparkバージョンに合わせて1.2.0へのリンク)
39. 並列コレクション 38
node1 node2 node3
executor executor executor
(executorが3つの場合)
(Python)
sc.parallelize(xrange(100), 3) #パーティション数3を指定
sc.count() # 3並列で処理される
0
1
2
3
:
32
33
34
35
36
:
65
66
67
68
69
:
99
RDD
パーティション
47. 分割可能でないファイル 46
node1 node2 node3
executor executor executor
data.gz
(block1)
data.gz
(block2)
data.gz
(block3)
HDFS
Spark
300MBのgzipファイルがあって
こんな風に分散されているとすると
51. 変換の連鎖
ここまで実行してもまだ
テキストを読みこんでない
(RDDからRDDへの変換操作だから)
50
(Python)
src = sc.textFile("hightemp.txt")
# ファイルを指定してRDDを作成する
tuples = src
.map(lambda x: x.split("t"))
.filter(lambda x: len(x) > 3)
# 100本ノック12
col1 = tuples.map(lambda x: x[0])
col2 = tuples.map(lambda x: x[1])
tabで分割
要素数4
以上
1列目
2列目
高知県t江川崎t41t2013-08-12
埼玉県t熊谷t40.9t2007-08-16
岐阜県t多治見t40.9t2007-08-16
山形県t山形t40.8t1933-07-25
山梨県t甲府t40.7t2013-08-10
:
54. もうちょっと変換 53
(続き)
# 2カラム目でソート
sorted_by_col2 = tuples.sortBy(lambda x: x[1])
# 3カラム目(数値)で降順ソート(100本ノック18)
sorted_by_col3 = tuples
.sortBy(lambda x: float(x[2]), False)
# 1コラム目の頻度の高い順にソート(100本ノック19)
from operator import add
frequency = col1.map(lambda x: (x, 1))
.reduceByKey(add)
.sortBy(lambda x: x[1], False)
# ここに書いたのは全部RDD変換定義のみで出力はない
昇順
降順
55. 何をしているか(100本ノック19)
54
高知県 江川崎 41 2013-08-12
埼玉県 熊谷 40.9 2007-08-16
岐阜県 多治見 40.9 2007-08-16
山形県 山形 40.8 1933-07-25
:
String
[高知県,江川崎,41,2013-08-12]
[埼玉県,熊谷,40.9,2007-08-16]
[岐阜県,多治見,40.9,2007-08-16]
[山形県,山形,40.8,1933-07-25]
:
高知県
埼玉県
岐阜県
山形県
:
String
(高知県,1)
(埼玉県,1)
(岐阜県,1)
(山形県,1)
:
(String, Int)
(高知県,[1].reduce(add))
(埼玉県,[1,1,1].reduce(add))
(岐阜県,[1,1].reduce(add))
(山形県,[1,1,1].reduce(add))
:
(String, Int)
map(x.split("t"))
filter(len(x)>3)
map(x[0])
map((x, 1))
reduceByKey(add)
Array[String]
col2
sortBy(x[1], False)
tuples
型名はScala準拠
(埼玉県,3)
(山形県,3)
(山梨県,3)
(群馬県,3)
:
frequency
56. もうちょっとアクション 55
(続き)
import json
def pp(obj):
print json.dumps(obj, ensure_ascii=False)
pp(sorted_by_col3.take(3))
[["高知県", "江川崎", "41", "2013-08-12"], ["埼玉県",
"熊谷", "40.9", "2007-08-16"], ["岐阜県", "多治見",
"40.9", "2007-08-16"]]
# 先頭から3個
pp(col1.takeOrdered(3))
["千葉県", "千葉県", "和歌山県"]
# 自然順序づけで小さい方から3個
pp(col1.top(3))
["高知県", "静岡県", "静岡県"]
# 自然順序づけで大きい方から3個
57. もうちょっとアクション 56
(続き)
for data in frequency.take(10):
print u'({}, {})'.format(*data)
(埼玉県, 3)
(山形県, 3)
(山梨県, 3)
(群馬県, 3)
(静岡県, 2)
(岐阜県, 2)
(愛知県, 2)
(千葉県, 2)
(高知県, 1)
(大阪府, 1)
59. テキストを読んで頻度カウント 58
val src = sc.textFile("tuples.tsv")
val tuples = src.
map(_.split("t")).
filter(_.size > 1)
val aFreq = tuples.
map( t => (t(0), 1L) ).reduceByKey(_+_)
val bFreq = tuples.
map( t => (t(1), 1L) ).reduceByKey(_+_)
val instancesFreq = tuples.
map( t => ((t(0), t(1)), 1L) ).reduceByKey(_+_)
ここからは Scala です
combinationtoffers
alabamathome
weddingtgreek
eviltdead
:
60. 何をしているか 59
combination offers
alabama home
wedding greek
evil dead
String
[combination, offers]
[alabama, home]
[wedding, greek]
[evil, dead]
(offers, 81)
(home, 36)
(greek, 24)
(dead, 20)
(String, Int)
map(_.split("t"))
filter(_.size>1)
Array[String]
(combination, 20)
(alabama, 40)
(wedding, 40)
(evil, 16)
(String, Int)
((combination, offers), 1)
((alabama, home), 5)
((wedding, greek), 5)
((evil, dead), 3)
((String, String), Int)
map
reduceByKey
map
reduceByKey
map
reduceByKey
tuples
aFreq
bFreq
instanceFreq
61. 頻度をつなぎあわせていく 60
val pmiSrc = instancesFreq.
map{ case ((a, b), t_f)
=> (a, (b, t_f)) }.
join(aFreq).
map{ case (a, ((b, t_f), a_f))
=> (b, (a, t_f, a_f)) }.
join(bFreq).
map{ case (b, ((a, t_f, a_f), b_f))
=> (a, b, t_f, a_f, b_f) }
Scalaだとパターンマッチで書けるけどPythonだと
map(lambda x: (x[1][0][0], x[0], x[1][0][1], x[1][0][2], x[1][1]))
pmi(a, b) = log
P(a, b)
P(a) P(b)
(aの文字列, bの文字列, [a, b]の頻度, aの頻度, bの頻度)
という組み合わせ(タプル)が欲しい
62. 何をしているか 61
map
((combination, offers), 1)
((alabama, home), 5)
((wedding, greek), 5)
((evil, dead), 3)
(combination, (offers, 1))
(alabama, (home, 5))
(wedding, (greek, 5))
(evil, (dead, 3))
(combination, 20)
(alabama, 40)
(wedding, 40)
(evil, 16)
(String, Int)
((String, String), Int)
(combination, ((offers, 1), 20))
(alabama, ((home, 5), 40))
(wedding, ((greek, 5), 40))
(evil, ((dead, 3), 16))
(String, ((String, Int), Int))
join
(String, (String, Int))
("a", 1)
("a", 2)
("b", 3)
("c", 4)
("a", "あ")
("a", "い")
("b", "か")
("d", "た")
から
※joinはinner joinするメソッド
("a", (1, "あ"))
("a", (1, "い"))
("a", (2, "あ"))
("a", (2, "い"))
("b", (3, "か")) を作る
(続く)
instanceFreq
aFreq
63. 何をしているか 62
map
(combination, ((offers, 1), 20))
(alabama, ((home, 5), 40))
(wedding, ((greek, 5), 40))
(evil, ((dead, 3), 16))
(offers, (combination, 1, 20))
(home, (alabama, 5, 40))
(greek, (wedding, 5, 40))
(dead, (evil, 3, 16))
(offers, 81)
(home, 36)
(greek, 24)
(dead, 20)
(String, Int)
(String, ((String, Int), Int)) (String, (String, Int, Int))
(offers, ((combination, 1, 20), 81))
(home, ((alabama, 5, 40), 36))
(greek, ((wedding, 5, 40), 24))
(dead, ((evil, 3, 16), 20))
(String,((String, Int, Int), Int))
join
(combination, offers, 1, 20, 81)
(alabama, home, 5, 40, 36)
(wedding, greek, 5, 40, 24)
(evil, dead, 3, 16, 20)
(String, String, Int, Int, Int)
map
=(a, b, [a, b]の頻度, aの頻度, bの頻度)
前ページ
最後
pmiSrc
bFreq
64. 計算する 63
val instancesAll = tuples.count
def calcDicountPmi(instance:Long, a:Long, b:Long) = {
def smooth (x:Long, y:Double) = {
x / (x + y) }
def discount(iTmp:Long, aTmp:Long, bTmp:Long) = {
smooth(iTmp, 1.0) * smooth(math.min(aTmp, bTmp), 1.0) }
def calcPmi(iTmp:Long, aTmp:Long, bTmp:Long) = {
import math.log
log(iTmp) - log(aTmp) - log(bTmp) + log(instancesAll) }
calcPmi(instance, a, b) * discount(instance, a, b)
}
val pmi = pmiSrc.map{
case (a, b, t_f, a_f, b_f)
=> (calcDicountPmi(t_f, a_f, b_f), a, b, t_f, a_f, b_f)
}
pmi.top(5).foreach(println)
(5.771154762538349,fat,greek,8,36,24)
(5.724583909571343,hong,kong,6,28,17)
(5.660412678732772,freaks,legged,4,16,9)
(5.632288650398451,greek,fat,5,20,19)
(5.590241876891969,scams,scams,3,8,7)
普通の定数
普通の関数
(クロージャ)
RDD変換
69. val s:String="hello"
s: String = hello
値の束縛
val i=1
i: Int = 1
型推論
var j=1
j: Int = 1
変数への代入
i=i+1 //間違い(再代入不可)
error: reassignment to val
j=j+1
j: Int = 2
s.contains("el")
res: Boolean = true
メソッド呼び出し
s contains "em"
res: Boolean = false
演算子スタイル
1+2 は 1.+(2) のこと
val t1=("a",3)
t1: (String, Int) = (a,3)
タプル
val t2="b"->4
t2: (String, Int) = (b,4)
2値タプルのシンタックスシュガー
t1._1
res: String = a
t1._2
res: Int = 3
タプルの要素
val nums = Seq(1,2,3)
nums: Seq[Int] = List(1, 2, 3)
シーケンス(Seqはファクトリでもある)
nums.map((x:Int)=>x*2)
res: Seq[Int] = List(2, 4, 6)
無名関数,マップ
nums.map(x=>x*2)
同上(型推論)
nums.map(_*2)
同上(プレースホルダー)
nums.reduce((x,y)=>x+y)
res: Int = 6
畳み込み
nums.reduce(_+_)
同上(プレースホルダー)
def even(x:Int):Boolean={
x%2==0
}
even: (x: Int)Boolean
関数(最後に評価した値が返り値)
nums.filter(even)
res: Seq[Int] = List(2)
フィルタリング
for (i<-nums) {
println(i)
}
1
2
3
繰り返し,標準出力
nums.foreach(println)
同上
val tuples=Seq(t1, t2)
tuples: Seq[(String, Int)] =
List((a,3), (b,4))
tuples.map{t=>
val i=t._2
t._1+i.toString
}
res: Seq[String] = List(a3, b4)
{} は複数行の無名関数を作る
tuples.map{t=>
t match {
case (s,i)=>s+i.toString
}
}
同上(パターンマッチング)
tuples.map{case (s,i)=>s+i.toString}
同上(パターンマッチング)
import scala.math
math.max(1,2)
res: Int = 2
パッケージのインポート
import scala.math._
max(1,2)
パッケージから全てインポート
s.split("l")
res: Array[String] = Array(he, "", o)
s(0)
res: String = he
配列のインデックスアクセス
nums.mkString(",")
res: String = 1,2,3
t1.mkString(",") //間違い
error: value mkString is not a member
of (String, Int)
s"${t1._1},${t1._2}"
res: String = a,1
文字列への変数の埋め込み
Scala Cheat Sheet
statement
result in the REPL
補足
79. よくつかうコマンド
hdfs dfs 〜
hdfsの操作
hdfs dfs –ls 〜
hdfs dfs –rm 〜
hdfs dfs –rm –r 〜
hdfs dfs –du –s –h 〜
hdfs dfs –mkdir 〜
hdfs dfs –cp 〜 〜
hdfs dfs –mv 〜 〜
78
80. よくつかうコマンド
hdfs dfs –put ローカルエントリ hdfsエントリ
HDFSにファイルを送る
hdfs dfs –get hdfsエントリ ローカルエントリ
hdfsからファイルを持ってくる
79
94. 並行処理について 93
(Python)
a = sc.parallelize(xrange(6), 2)
b = sc.parallelize(xrange(6), 1)
from operator import sub
a.reduce(sub)
3
b.reduce(sub)
-15
Editor's Notes ちょっとどこかを変えたらどこかが変わる、みたいな習熟が難しい 3億7千万行 pmi計算
30分 24並列
12分 77並列
昔はhadoopコマンドで全部できた。今は分離している。 hadoop fs が deprecated にされた?