第2回 ``learning spark'' 読書会 第3章 ``programming with rdds

51
2 “Learning Spark” 読書会 Chap. 3: “Programming with RDDs” – RDDs プログラミング @data sciesotist 2015/3/29

Upload: sciesotist-data

Post on 20-Aug-2015

781 views

Category:

Data & Analytics


0 download

TRANSCRIPT

Page 1: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

第 2回 “Learning Spark” 読書会

Chap. 3: “Programmingwith RDDs”

– RDDsプログラミング –

@data sciesotist

2015/3/29

Page 2: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

1 “This chapter introduces...”• Sparkにおけるデータ (RDDs) の取り扱いを紹介する

• Sparkにおけるすべての操作はRDDsの生成、RDDsの変換、RDDsの計算であらわされる

• データサイエンティストもエンジニアもこの章を読んでね

• できれば、手元で動作確認しながら読んで欲しい

• コードはGitHubで公開している

……だそうです。

1

Page 3: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

2 “RDD Basics” (1)• RDD (Resilient Distributed Datasets) はイミュータブル*1な分散コレクションである

• RDDは複数のパーティションに分割され、それぞれ異なるノードで処理される

• 外部のデータセットを読み込むか、コレクションオブジェクトを分散することで生成できる

*1 Immutable: 不変2

Page 4: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

3 “RDD Basics” (2)

>>> lines = sc.textFile("README.md")

• 作成したRDDsには2つの処理が適用できる

■ Transformations: RDDsから別の新しいRDDsを生成する

■ Actions: RDDsに対して処理を適用した結果を出力する

3

Page 5: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

4 “RDD Basics” (3) Transformations• Transformationsの例として、条件に一致する要素を抽出して、新しいRDDを作成するフィルタリングがある

>>> pythonLines = lines.filter(lambda line:

"Python" in line)

4

Page 6: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

参考: 主なTransformations (1)

関数 機能map(func) 各要素にfuncを適用した結果

を新しいRDDとして返すfilter(func) 各要素のうちfuncに一致する

ものを抽出して返すflatMap(func) 各要素にfuncを適用した結果

を展開 (flatten) して返すmapPartitions(func) 各パーティションに対して

funcを適用した結果を返すmapPartitionsWithIndex(func) パーティションのインデック

スを指定してmapPartitionsを実行した結果を返す

5

Page 7: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

参考: 主なTransformations (2)

関数 機能sample(Rep,fraction,seed) RDDから条件を指定してサンプ

リングした結果を返すunion(Dataset) データを結合した結果を返すintersection(Dataset) データ間の共通要素を返すdistinct() データセットから重複したデー

タを除いた結果を返すgroupByKey() Key-Value形式のデータについ

てKeyを集計した結果を返すreduceByKey(func) Key-Value形式のデータについ

てfuncを適用した結果を返す

6

Page 8: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

参考: 主なTransformations (3)

関数 機能aggregateByKey(zero)(seq,comb) Key-Value形式のデータについ

て、Valueに処理を行い集計した結果を返す

sortByKey() Key-Value形式のデータについてKeyでソートした結果を返す

join(Dataset) データセットどうしをJoinした結果を返す

cogroup(Dataset) データセットどうしを同じKeyでグルーピングした結果を返す

cartesian(Dataset) データセットどうしを直積でJoinした結果を返す

7

Page 9: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

参考: 主なTransformations (4)

関数 機能pipe(command) 各要素にシェルコマンドを適用

した結果を返すcoalesce(num) 指定した数までパーティション

数を減らすrepartition(num) データを再配置する

repartitionAndSort

WithinPartitions(partitioner)

データを指定した方法で再配置し、ソートした結果を返す

……とがんばってまとめたものの、4章に表があるんだよねorz

8

Page 10: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

5 “RDD Basics” (4) Actions• Actionsの例として、RDDから最初の1要素を抽出するfirst()がある

>>> pythonLines.first()

• Actionsは、処理結果をドライバプログラムに返すか、外部ストレージシステム (HDFSなど)に保存する

9

Page 11: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

6 “RDD Basics” (5) TransformationsとActionsの違い

• SparkがRDDsを処理する際の動作が異なる

• RDDsを定義しても、最初に使用される時まで処理は実行されない (lazy fashion)

• 即座に処理すると、メモリの無駄になる• first()アクションも、条件にマッチする最初の1行までしかデータを読み込まない

10

Page 12: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

7 “RDD Basics” (6) 永続化 (1)• RDDsはActionsの実行のたびに処理される

• RDDsを繰り返し使いたい場合はRDD.persist()を使う

Level Space used CPU time In memory On disk

MEMORY ONLY 高 低 Y N

MEMORY AND DISK 高 中 一部 一部

MEMORY ONLY SER 低 高 Y N

MEMORY AND DISK SER 低 高 一部 一部

DISK ONLY 低 高 N Y

MEMORY ONLY 2 など パーティションの複製を作成する

OFF HEAP Tachyonファイルシステムに保存する

11

Page 13: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

8 “RDD Basics” (6) 永続化 (2)• データの一部をメモリ上に永続化し、繰り返しクエリを発行する時に使うことが多いだろう

>>> pythonLines.persist()

>>> pythonLines.count()

3

>>> pythonLines.first()

u’high-level APIs in...

12

Page 14: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

9 “RDD Basics” のまとめ1. データからRDDsを作る

2. Transformationsは新しいRDDsを作る

3. persist()でRDDsを永続化できる

4. ActionsはRDDsを分散処理する

• cache()もデフォルト設定でpersist()を実行したのと同じ結果を得られる

13

Page 15: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

10 “Creating RDDs” (1)

• RDDsの作り方には2種類ある

1. 外部データセットを読み込む方法

2. コレクションを並列化 (Parallelizing)させる方法

• 簡単なのはsc.parallelize()を使う方法 (上記2)

• ファイルからの読み込みはすでに行ってきた14

Page 16: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

11 “Creating RDDs” (2)

# Pythonの場合lines = sc.parallelize(["pandas","i like pandas"])

# Scalaの場合val lines =

sc.parallelize(list("pandas","i like pandas"))

# Javaの場合 (実際には1行で)JavaRDD<String> lines =

sc.parallelize(Arrrays.asList("pandas","i like pandas"));

15

Page 17: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

12 “RDD Operations” (1)• TransformationsとActionsがある (再掲)

• TransformationsはRDDsを返す

• Actionsは他のデータ型を返す

• SparkにおけるTransformationsとActionsの動作の違いを理解することは重要なので、詳しく見ていく

16

Page 18: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

13 “RDD Operations” (2) Transformations

• Transformationsは新しいRDDsを生成する

• RDDsに対してActionsが実行されるまでは、処理されない (Lazy Evaluation;遅延評価)

• 多くのTransformationsは “要素指向”(element-wise)で、各要素に対して作用する

• ログファイルlog.txtからエラーメッセージだけを抽出する例を見ていく

17

Page 19: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

14 “RDD Operations” (3) Transformations

# Pythonの場合inputRDD=sc.textFile("log.txt")errorsRDD=inputRDD.filter(lambda x: "error" in x)

# Scalaの場合val inputRDD=sc.textFile("log.txt")val errosRDD=inputRDD.filter(line =>

line.contains("error"))

18

Page 20: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

15 “RDD Operations” (4) Transformations

# Javaの場合JavaRDD<String> inputRDD = sc.textFile("log.txt")JavaRDD<String> errorsRDD = inputRDD.filter(new Function<String,Boolean>(){public Boolean call(String x){

return x.contains("error");}});

• filter()はinputRDDを変更しない• 新しいerrorsRDDオブジェクトが生成される

19

Page 21: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

16 “RDD Operations” (5) Transformations

• 今度はunion()を用いてwarningまたはerrorを検索する

# Pythonの場合warnRDD=inputRDD.filter(lambda x: "warning" in x)badLinesRDD=errorsRDD.union(warnRDD)

• union()は2つのRDDsを処理する

• RDDsを生成する際の操作手順を保持しているので、RDDsが失われた際に復旧できる

20

Page 22: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

17 “RDD Operations” (6) Transformations

inputRDD

errorsRDD

warnRDD

badLinesRDD

filter()

filter()

union()

Lineage GraphまたはDAG (有向無閉路グラフ) というらしい

21

Page 23: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

18 “RDD Operations” (7) Actions

• データセットに対して “何か” を実行し、出力するのがActions

• ログ処理の例を続けて、badLinesRDDの情報を出力する方法を見ていく

• count()はデータの件数を返す• take()は指定した件数の要素を返す

22

Page 24: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

19 “RDD Operations” (8) Actions

# Pythonの例print "Input had" + badLinesRDD.count()+ "concerning lines"print "Here are 10 examples:"for line in badLinesRDD.take(10):print line

# Scalaの例println("Input had" + badLinesRDD.count()+ "concerning lines"println("Here are 10 examples:")badLinesRDD.take(10).foreach(println)

23

Page 25: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

20 “RDD Operations” (9) Actions

# Javaの例System.out.println("Input had"+ badLinesRDD.count() + "concerning lines")System.out.println("Here are 10 examples:")for(String line: badLinesRDD.take(10)){System.out.println(line);}

• 注意: collect()はRDDs内の全データをメモリに展開するため、サイズの大きいRDDsに対して使用すべきではない

24

Page 26: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

21 “RDD Operations” (10) Actions

• 大量データをHDFSやAmazon S3などのストレージに保存するにはsaveAsTextFile()やsaveAsSequenceFile()などを使う

• 注意: アクションを実行するたびに、RDDsはゼロから再生成される

• これを避けるために、persist()を用いてRDDsを永続化することができる

25

Page 27: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

22 “RDD Operations” (11) Lazy Evaluation

• RDDsは「遅延評価」される• HaskelやLINQのような関数型言語ではよくあるパラダイム• 内部では処理手順だけが (まずは)記録される• sc.textFile()を実行しても、すぐにはデータは読み込まれない• (ディスクアクセスなどを考慮して)処理手順に悩む必要がなく、効率的な開発、実行ができる

26

Page 28: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

23 “Passing Functions to Spark” (1)• ドライバプログラムからSparkに処理を渡すために

Passing Functionsが用いられる

• Pythonでは3つの形式で利用できる (ラムダ式、トップレベル関数、ローカル関数)

word = rdd.filter(lambda s: "error" in s)def containsErrot(s):return "error" in s

word = rdd.filter(containsError)

• 全てのデータがワーカーノードに送信されるので注意

27

Page 29: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

参考: データの一部を参照するつもりで大変なことになる例

• フィルタリングの結果ではなく、データ全体への参照になっている?

class SearchFuntions(object):

def __init__(self,query):

self.query = query

def isMatch(self,s):

return self.query in s

def getMatchesFunctionReference(self,rdd):

return rdd.filter(self.isMatch)

def getMatchesMemberReference(self,rdd):

return rdd.filter(lambfs x: self.query in x)

28

Page 30: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

参考: 正しくデータの一部だけを参照する例

class WordFunctions(object):

...

def getMatchesNoReference(self,rdd):

query = self.query

return rdd.filter(lambda x: query in x)

• プログラミングはよくわかりませんが、変数のスコープとかそういう話?(当日) 誰か教えてください

29

Page 31: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

24 “Passing Functions to Spark” (2)• Scalaでも同様に全データを参照しないよう注意する

class SearchFunctions(val query:String){def isMatch(s:String): Boolean = {s.contains(query)

}def getMatchesFunctionReference(rdd: RDD[String]): RDD[String] = {# 注意: 全データを参照してしまうrdd.map(isMatch)

}def getMatchesFieldReference(rdd: RDD[String]): RDD[String] = {# 注意: 全データを参照してしまうrdd.map(x => x.split(query))

}def getMatchesNoReference(rdd: RDD[String]): RDD[String] = {# データを一部だけ参照するval query_ = this.queryrdd.map(x => x.split(query_))

}}

30

Page 32: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

25 “Passing Functions to Spark” (3)• Javaではorg.apache.spark.api.java.functionパッケージのインターフェースの実装にあたる

# 匿名内部クラスを使う場合RDD<String> errors = lines.filter(

new Function<String,Boolean>(){

public Boolean call(String x){

return x.contains("error");}

}

31

Page 33: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

26 “Passing Functions to Spark” (4)

# 名前付きクラスを使う場合class ContainsError implements

Function<String,Boolean>(){

public Boolean call(string x){

return x.contains("error");}

}

RDD<String> errors =

lines.filter(new ContainsError());

32

Page 34: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

27 “Passing Functions to Spark” (5)

# function class with parametersを使う場合class Contains implements

Function<String,Boolean>{

private String query;

public Contains(String query){

this.query = query;}

public Boolean call(String x){

return x.contains(query);}

}

33

Page 35: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

28 “Passing Functions to Spark” (6)• Java 8ではラムダ式が使えるので、簡潔な実装になる

• 詳しくはOracleやDatabrickのページを見てね

RDD<String> errors = lines.filter(

s -> s.contains("error"));

• 匿名内部クラスでもラムダ式でも、final修飾子のついた変数をSparkに渡すことができる(というのがどう良いことなのかよくわかりませんが)

34

Page 36: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

29 “Common Transformations andActions” (1)

• RDDsに対する基本的なTransformationsとActionsを見てきた

• ここからは、RDDsのタイプごとに、固有の操作を紹介していく

35

Page 37: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

30 “Common Transformations andActions” (2) Basic RDDs

• すべてのタイプのRDDsに適用可能な操作を見ていく

• まずはTransformations

• map()はRDDsの各要素に処理を適用し、新しいRDDsに出力する

• map()が返す値は必ずしも元のRDDsと同じ型ではない

• filter()はRDDsの各要素のうち、フィルタ条件を満たすものだけを新しいRDDsに出力する

36

Page 38: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

31 “Common Transformations andActions” (3) Basic RDDs

inputRDD

{1,2,3,4}

map(x=>x*x)

filter(x=>x!=1)

mappedRDD

{1,4,9,16}

filteredRDD

{2,3,4}

37

Page 39: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

32 “Common Transformations andActions” (4) Basic RDDs

# Pythonでのmap処理の例nums = sc.parallelize([1,2,3,4])squared = nums.map(lambda x: x * x).collect()for num in squared:

print "%i " % (num)

# Scalaでのmap処理の例val input = sc.parallelize(List(1,2,3,4))val result = input.map(x => x * x)println(result.collect().mkString(","))

38

Page 40: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

33 “Common Transformations andActions” (5) Basic RDDs

# Javaでのmap処理の例JavaRDD<Integer> rdd = sc.parallelize(Arrays.asList(1,2,3,4));JavaRDD<Integer> result = rdd.map(

new Function<Integer,Integer>(){public Integer call(Integer x) {return x*x; }

});

System.out.println(StringUtils.join(result.collect(),","));

39

Page 41: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

34 “Common Transformations andActions” (6) Basic RDDs

• flatMap()はRDDsの個々の要素に処理を行った後、要素を展開 (flatten) して出力する

inputRDD{“coffee panda”, “happy

panda”, “happiest

panda party”}

map(tokenize)

flatMap(tokenize)

mappedRDD{[“coffee”, “panda”],[“happy”,

“panda”],[“happiest”, “panda”, “party”]}

filteredRDD{“coffe”, “panda”, “happy”, “panda”,

“happiest”, “panda”, “party”}

tokenize(“coffe panda”=>List(“coffee”,”panda”)

40

Page 42: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

35 “Common Transformations andActions” (7) Basic RDDs

• 以下の4つの操作は、2つのRDDsが、同じタイプである必要がある

• distinct(): 重複を除去する (負荷が高い)

• union(): RDDsを結合する (重複を許容する)

• intersection(): 共通要素を出力する(重複は排除される、さらに負荷が高い)

• subtract(): 共通しない要素を出力する

41

Page 43: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

36 “Common Transformations andActions” (8) Basic RDDs

• cartesian()でRDDsを直積結合できる

• 片方のRDDsの1要素ごとに、もう一方のRDDsのすべての要素を対応付けて出力する

• ユーザー間の類似性の判定などを行う際に便利• 非常に負荷が高い

42

Page 44: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

37 “Common Transformations andActions” (9) Basic RDDs

• 続いてActionsについて

• もっとも一般的なActionsはreduce()

• 2つの要素を処理して、1つの要素として返す

• 似たようなものとしてfold()がある

• 違いはカウンタ (zero value)を与えるかどうか

• 移動平均を算出するときなどに必要43

Page 45: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

38 “Common Transformations andActions” (10) Basic RDDs

# Pythonの場合sum = rdd.reduce(lambda x,y: x + y)

# Scalaの場合val sum = rdd.reduce((x,y) => x + y)

# Javaの場合Integer sum = rdd.reduce(new Function2<Integer,Integer,Integer>(){public Integer call(Integer x, Integer y)

{ return x + y; } });

44

Page 46: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

39 “Common Transformations andActions” (11) Basic RDDs

• aggregate()は処理対象のRDDsと処理後のRDDsが同じ型である必要がない

• 引数は、(1) zero value、(2)各要素の処理、(3) 集約処理

• collect()はRDDsのすべての要素を返す

• take(n)はデータをn個抽出する

• top(n)はデータを先頭からn件抽出する

• takeSample()はデータをサンプリング抽出する45

Page 47: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

40 “Common Transformations andActions” (12) Basic RDDs

• foreach()は個々の要素に処理を実行し、返り値はない

• count()は要素の件数を返す• countByValue()は要素ごとにmapし、件数を返す

46

Page 48: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

41 “Common Transformations and Actions”(13) Converting Between RDD Types

• 特定の型のRDDsにしか適用できない関数もある (mean()やjoin()など)

• Scalaでは型変換は暗黙のうちに行われる

• Javaでは型変換を明示的に行う必要がある

• Pythonではすべての関数が同じpyspark.RDDクラスに実装されている

47

Page 49: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

42 “Persistence” (1)• persist()でRDDsを永続化できる

• 反復処理などを行う際に便利

• 一部が消失しても、再計算して復旧できる

• あらかじめ複製を作っておくこともできる

• ScalaとJavaのデフォルト動作は、シリアライズせずにヒープ領域に保存する

• Pythonではデフォルトでシリアライズして保存する

48

Page 50: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

43 “Persistence” (2)• インメモリファイルシステムであるTachyonに保存することもできる

• 詳細はTachyonのドキュメントを参照

• persist()は最初のActionsの前に実行する

• メモリのサイズを超えるRDDsを永続化する場合、古い領域から削除され、アクセスの際に再計算される

• unpersist()で永続化を解除できる

49

Page 51: 第2回 ``Learning Spark'' 読書会 第3章 ``Programming with RDDs

44 “Conclusion”• 3章ではRDDsの実行モデルと多数のさまざまな処理を紹介した

• 3章がわかれば、Sparkのコアは理解している

• 4章ではRDDsの特殊な形態であるKey-Valueペアについて扱う

• その後の章で、さまざまなデータソースの扱いやSparkContextの応用的な話題を取り上げる

50