Apache Spark チュートリアル
-
Upload
k-yamaguchi -
Category
Software
-
view
3.928 -
download
4
Embed Size (px)
Transcript of Apache Spark チュートリアル

Apache Spark チュートリアル
東北大学 乾・岡崎研究室山口健史
2015-04-28

MapReduceのはなし
1

単語の頻度カウントを例に
2

素朴な実装 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)

巨大なファイルだと…… 4
:
for line in opened_file:
for word in some_splitter(line):
frequency[word] += 1
:
巨大なファイル メモリが
足りない!!

入力を分割する 5
頻度ファイル
ファイル頻度
同じ単語が両方のファイルに入ってるので統合するときになにか工夫しないとやっぱりメモリが足りない

出力を分割する 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
もう一度読む
残り半分

組み合わせる 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
並列可能

これだとスケールアウトする
並列に実行できるようになっている
ファイル読み込みの部分が並列実行可能
それでもメモリが足りなくなるなら分割数を増やせばいい
100〜10,000分割でも動くアルゴリズム
8

でもHadoopは簡単じゃなかった
簡単なことにたくさんソースを書く
Hadoopの仕組みを知らないと作れない
たいていのフレームワークはそうだけど……
9

でも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
個人の感想です

Apache Spark のはなし
11

Sparkとは
Hadoop MapReduceにインスパイアされた分散処理基盤
Hadoop-HDFSのような分散ファイルシステムをベースとして利用する
Scala製。PythonやJavaでも使える
12

Sparkのうれしいところ
ScalaやPythonのコードがMapReduceのように動く
SparkやHDFSがなにをしてくれるかは知らなくても書ける(理解しておいた方はよい)
途中のデータをメモリに保持できる
Sparkが高速と言われる理由
Hadoop-MapReduceは必ずディスク保存だった
13

Sparkは簡単
例)タブ区切りの5カラム目を切り出し”拡散希望”が含まれるものを抽出
これで分散処理される
14
(Python)
sc.textFile(u'tweets.txt').\
map(lambda x: x.split('\t')[4]).\
filter(lambda x: u'拡散希望' in x)

Sparkは簡単
例)タブ区切りの5カラム目を切り出し”拡散希望”が含まれるものを抽出
これで分散処理される
15
(Scala)
sc.textFile("tweets.txt").
map(_.split("\t")(4)).
filter(_.contains("拡散希望"))

研究室の環境について
研究室のマシンではSparkクラスタが常時起動しているわけではない
Hadoop-YARNにリソースを要求して必要な時にクラスタを起動する
16

クラスタの起動と解消
要求/確保したリソースでクラスタを構成するexecutor(並列処理のワーカー)6個分のメモリを要求している
spark-shellの終了がクラスタの解消
終了しないとリソースを確保した状態のまま
17
spark-shell --master yarn-client --num-executors 6

Sparkのコンポーネント 18
node00 node01 node02 node03
node04 node05 node20
driverexecutor
executor
masterexecutor
executor
executor
executor
executor
executor
executor
executor
executor
(shell実行モードの場合)

driver19
node00 node01 node02 node03
node04 node05 node20
driverexecutor
executor
masterexecutor
executor
executor
executor
executor
executor
executor
executor
executor
利用者が見ているコンソール(spark-shell)
WebUIも提供する
定数・関数などの定義(定義したものは各executorに分散され共有される)

executor20
node00 node01 node02 node03
node04 node05 node20
driverexecutor
executor
masterexecutor
executor
executor
executor
executor
executor
executor
executor
executor
実際にジョブを実行している部分

master21
node00 node01 node02 node03
node04 node05 node20
driverexecutor
executor
masterexecutor
executor
executor
executor
executor
executor
executor
executor
executor
SparkではなくHadoop YARNのコンポーネント
なにをしているかはわかってないYARN Resource Managerとやりとりしている(?)

Sparkを体験する
22

かんたんSpark体験
ローカルで2つのexecutorで実行する
23
#Mac Homebrewでbrew install apache-spark
#Scalaシェルspark-shell --master "local[2]"
#Pythonシェルpyspark --master "local[2]"

かんたん並行処理
a.reduceは2プロセスでb.reduceは1プロセスで実行される
Pythonプロセスと実行時間で確認
25
(Python)
# 0から999999999までの総和を計算するfrom operator import add
a = sc.parallelize(xrange(1000000000), 2)
b = sc.parallelize(xrange(1000000000), 1)
a.reduce(add)
499999999500000000
b.reduce(add)
499999999500000000

かんたん並行処理
Scalaではスレッドが使われる
26
(Scala)
// 0から999999999までの総和を計算するval a = sc.parallelize(1L until 1000000000, 2)
val b = sc.parallelize(1L until 1000000000, 1)
a.reduce(_ + _)
499999999500000000
b.reduce(_ + _)
499999999500000000

SparkContext
scはspark-shell起動時に生成されるSparkContext型のインスタンス
SparkContextはジョブの実行状態や参加しているexecutorの状態を持つ
27
a = sc.parallelize(xrange(1000000000), 2)

RDD "Resilient Distributed Dataset"
分散データを扱う抽象コレクション
データの実体がどこにあるのかを意識しない
並列動作する変換操作(map, filter,,)
キャッシュの選択肢
メモリにキャッシュ, ディスクにキャッシュ
28

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のメモリ上でインスタンス化している
という意味

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での比較

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から具体的なデータへ変換する
(ローカルコレクション, 値, ファイル)

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つの値からコレクションを返す」関数を元に複数データを生成

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)]

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"]))] 相当

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にはない

RDD : 資料
http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.rdd.RDD
http://spark.apache.org/docs/latest/api/python/pyspark.html#pyspark.RDD
http://www.ne.jp/asahi/hishidama/home/tech/scala/spark/RDD.html
36
(mezcalのSparkバージョンに合わせて1.2.0へのリンク)

RDDと並列可能性
37

並列コレクション 38
node1 node2 node3
executor executor executor
(executorが3つの場合)(Python)
sc.parallelize(xrange(100), 3) #パーティション数3を指定sc.count() # 3並列で処理される
0123:32
33343536:65
66676869:99
RDD
パーティション

並列コレクション 39
(Python)
sc.parallelize(xrange(100), 1) #パーティション数1を指定sc.count() # 1並列で処理される
node1 node2 node3
executor executor executor
012:9899
RDD
(executorが3つの場合)

分割可能なファイル
ローカリティを考慮してなるべくデータを保持するノードで処理する
40
(executorが3つの場合)(Python)
sc.textFile("/work/test/data.txt") # 128MB未満sc.count() # 1並列で処理される(HDFS上のファイルの場合)
node1 node2 node3
executor executor executor
data.txt
RDD

分割可能なファイル
HDFSはファイルを128MBごとに分割して分散させている(研究室の設定では)
プレーンテキストとbzip2ファイルが分割可能なファイル形式
分割可能なファイルはブロック単位に並列分散処理される
41

分割可能なファイル 42
(executorが3つの場合)(Python)
sc.textFile("/work/test/large.txt") # 300MBぐらいsc.count() # 3並列で処理される
node1 node2 node3
executor executor executor
large.txt(block1)
large.txt(block2)
large.txt(block3)
RDD

分割可能なファイル
128MBの境界で文が分かれてしまわないの?
心配いらない改行をまたぐ部分がネットワークを移動して文単位で処理される
43

分割可能なファイル
境界をまたぐ分が移動してから処理が始まる
44
(executorが3つの場合)(Python)
sc.textFile("/work/test/large.txt") # 300MBぐらいsc.count() # 3並列で処理される
node1 node2 node3
executor executor executor
large.txt(block1)
large.txt(block2)
large.txt(block3)
RDD

分割可能でないファイル
分割可能でないファイル
例えばgzip圧縮ファイルとか
128MBを超えるファイルは後ろのブロックがすべてネットワーク越しにコピーされてから処理が始まる=分散されない!!
128MBを超えないぐらいの大きさに抑える
トータルのパフォーマンスはこの置き方が一番いい
45

分割可能でないファイル 46
node1 node2 node3
executor executor executor
data.gz(block1)
data.gz(block2)
data.gz(block3)
HDFS
Spark
300MBのgzipファイルがあってこんな風に分散されているとすると

分割不可能なファイル 47
(executorが3つの場合)(Python)
sc.textFile("/work/test/data.gz") # 300MBぐらいsc.count() # 1並列でしか処理されない
node1 node2 node3
executor executor executor
data.gz(block1)data.gz
(block2)data.gz(block3)
data.gz(block2)
data.gz(block3)
ネットワーク越しのコピー
RDD

分割可能でないファイル
この形が一番効率がいい
48
(executorが3つの場合)(Python)
sc.textFile("/work/test/p*.gz") # 該当する3ファイル(128MB未満)sc.count() # 3並列で処理される
node1 node2 node3
executor executor executor
p0.gz p1.gz p2.gz
RDD

Spark実例
(頻度カウント再び)
49

変換の連鎖
ここまで実行してもまだテキストを読みこんでない(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江川崎\t41\t2013-08-12埼玉県\t熊谷\t40.9\t2007-08-16岐阜県\t多治見\t40.9\t2007-08-16山形県\t山形\t40.8\t1933-07-25山梨県\t甲府\t40.7\t2013-08-10
:

アクション
いつの間にか並行処理されている
51
(続き)
col1.count()
24
# ここではじめてファイルを読みこんで# split->filter->map->count が実行される
col1.saveAsTextFile("col1")
# またファイルを最初から読みこんで# split->filter->map->saveAsTextFile が実行される
# これでcol1というテキストが作成される……かと思いきや# col1というディレクトリができている# ローカル実行だと2ファイルある(part-00000 part-00001)
かもしれないし、されないかもしれない

キャッシュの制御 52
(続き)
# 毎回ファイルを読みこむのは効率が悪い
col1.cache()
# これで可能ならばメモリに保持するようになった
col1.count()
24
# またファイルから読んでsplit->filter->map->count
col1.distinct().count()
12
# 異なり数を数える(100本ノック17)
col1.take(2)
[u'\u9ad8\u77e5\u770c', u'\u57fc\u7389\u770c']
ここではもうファイルを読んでいない

もうちょっと変換 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変換定義のみで出力はない
昇順
降順

何をしているか(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

もうちょっとアクション 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個

もうちょっとアクション 56
(続き)
for data in frequency.take(10):
print u'({}, {})'.format(*data)
(埼玉県, 3)
(山形県, 3)
(山梨県, 3)
(群馬県, 3)
(静岡県, 2)
(岐阜県, 2)
(愛知県, 2)
(千葉県, 2)
(高知県, 1)
(大阪府, 1)

Spark実例
(PMI計算)
57

テキストを読んで頻度カウント 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 です
combination\toffersalabama\thomewedding\tgreekevil\tdead:

何をしているか 59
combination offersalabama homewedding greekevil 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

頻度をつなぎあわせていく 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) = logP(a, b)
P(a) P(b)
(aの文字列, bの文字列, [a, b]の頻度, aの頻度, bの頻度)という組み合わせ(タプル)が欲しい

何をしているか 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

何をしているか 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

計算する 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変換

無駄な処理をしているのでは?
その通り。aの頻度とbの頻度がメモリにのりきるならMap(=辞書)として具体化した方が速い
ただしこれはスケールアウトしない
64
val aFreqMap = aFreq.collectAsMap
val bFreqMap = bFreq.collectAsMap
val pmiSrc = instancesFreq.map{
case ((a, b), t_f) =>
(a, b, t_f, aFreqMap(a), bFreqMap(b))
}

まとめ
Sparkでもちゃんとスケールアウトするように書くのはコツが要る
それでも並列処理を書くコストはずっと小さくなっている
Sparkに求める事を意識して書き分ける
並列処理を簡単に書きたい のか
大規模データでもスケールアウトさせたい のか
65


補遺
67

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 Sheetstatement
result in the REPL
補足


HDFSのはなし
70

HDFSとは?
Hadoop Distributed File System
分散仮想ファイルシステム
普通のファイルシステムとしてマウントできない(実用レベルでは)
71

HDFSクラスタ
node7
HDFS72
node1 node2 node3 node4
node5 node6 node8
300MBの
ファイル登録

HDFSクラスタ
node7
HDFS73
node1 node2 node3 node4
node5 node6 node8block3
block1
block2
登録
128MBごとの固まりに分ける

HDFSクラスタ
node7
HDFS74
node1 node2 node3 node4
node5 node6 node8block3
block1
block2
登録
block1
block1
block1block2
block2 block2
block3
block3
block3
3重に複製されてクラスタに保管される

HDFS
なぜ128MBごとに分割するか?
MapReduceではデータが分散されている必要がある
MapReduceでは各ワーカーの処理がメモリーに乗りきる必要がある
なぜ複製を作るのか?
障害耐性が高くなる
分散処理の効率がよくなる(データの移動が少なくなる)
75
Sparkでも同じ!

Hadoop-HDFSで使うコマンド
76

よくつかうコマンド
最近流行りのコマンド サブコマンド オプション
の形をしている。
hdfs コマンド
ファイルシステムに何かする
77

よくつかうコマンド
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

よくつかうコマンド
hdfs dfs –put ローカルエントリ hdfsエントリ
HDFSにファイルを送る
hdfs dfs –get hdfsエントリ ローカルエントリ
hdfsからファイルを持ってくる
79


MapReduceの様子
81

MapReduce82
あらかじめデータは分散されて置かれている
がノード
データ

MapReduce : Map & Partition83
分割する(ハッシュの剰余などを使う)

MapReduce : Shuffle & Sort84
集める

MapReduce : Reduce85
ノードことに処理する

MapReduceの様子
もう少しうまくやる
86

MapReduce87

MapReduce : Combine88
処理と分割(ハッシュの剰余などを使う)

MapReduce : Shuffle & Sort89
剰余が同じデータを同じノードに集める

MapReduce : Reduce90
処理する


並行処理について
92

並行処理について 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

b
partiion 0
a
partiion 1
partiion 0
RDD94
0 1 2 3 4 5
0 1 2
3 4 5

a
partition 1
partition 0
a.reduce(sub)95
0 1 2
3 4 5
((0 - 1) - 2) =reduce
((3 - 4) - 5) =reduce
-3
-6
reduce
3

b
partition 0
b.reduce(sub)
順番で結果が変わるようなreduceを並行・並列で実行してはいけない
96
0 1 2 3 4 5
(((((0 - 1) - 2) - 3) - 4) - 5) =
reduce
-15