より良い読書体験: PySpark の基本入門 (2): RDD とその共通演算子 - Nuggets (juejin.cn)
目次
RDD の概要
RDD (Resilient Distributed Dataset) は、Spark の最も基本的なデータ抽象化であり、並行して計算できる不変で分割可能な要素のコレクションを表す、回復力のある分散データセットです。
データセット: データを保存するためのデータセット。
分散: RDD 内のデータは分散方式で保存され、分散コンピューティングに使用できます。
復元力: RDD 内のデータはメモリまたはディスクに保存可能
RDDの特徴
- RDD はパーティション化されており、RDD パーティションはデータ ストレージの最小単位です。
- RDD のメソッドはそのすべてのパーティションに作用します
- RDD間には依存関係(血縁関係)がある
- Key-Value型のRDDはパーティショナを持つことができます(Key-Value型。RDDに格納されるデータはタプルであることを意味します)
- RDD パーティション プランは、データが配置されているパーティショナーにできる限り近くなります (つまり、エグゼクターとデータを保存するブロックはできる限り同じサーバー上にある必要があり、ディスクの読み取りのみが必要になります)ネットワーク IO は必要ありませんが、並列コンピューティング機能を確保する必要があります。次に、ローカル読み取りを実現します)
RDDコーディング
スパークコンテキスト
SparkContext オブジェクトは、RDD プログラミングのプログラム エントリ オブジェクトです。
基本的に、プログラミングにおける SparkContext の主な機能は、最初の RDD を作成することです。
conf = SparkConf().setAppName("test").setMaster("local[*]") # 构建SparkContext对象 sc = SparkContext(conf=conf)
conf 構成について:
1.
setMaster()
ローカルで実行している場合はローカルと記入し、yarn に送信する場合は記入します。"yarn"
2. 依存ファイルをセットアップします。
conf.set("spark.submit.pyFiles", 文件)
ファイルの形式は .py または .zip です。
Linux で送信する場合は、次の方法で
--py-files .zip/.py
依存ファイルを送信します。
RDD の作成方法
並列作成(並列化)
方法:
sc.parallelize(集合对象,分区数)
rdd = sc.parallelize([1, 2, 3, 4, 5, 6, 7, 8, 9]) print("默认分区数: ", rdd.getNumPartitions()) rdd = sc.parallelize([1, 2, 3], 3) print("分区数: ", rdd.getNumPartitions())
getNumPartitions
: 現在のrddのパーティション数を取得します。
読み取りファイルの作成 (textFile )
方法:
sc.textFile(文件路径,最小分区数)
ここでのファイル パスは相対パスまたは絶対パスにすることができ、ローカル データまたは HDF データを読み取ることができます。
Windows からローカルでファイルを読み取る場合と、Linux への SSH 接続でファイルを読み取る場合は、ファイル パスが異なることに注意してください。
Windows ではファイル パスをローカルに直接書き込むことができます。Linux のパスは
file://
+です。/home/wuhaoyi/......
# 读取本地文件 sc.textFile("../data/input/words.txt") # 设置最小分区数 sc.textFile("../data/input/words.txt", 3) # 注意,设置的最小分区数太大时会失效 sc.textFile("../data/input/words.txt", 100) # 设置100分区不会生效 # 读取hdfs文件 sc.textFile("hdfs://10.245.150.47:8020/user/wuhaoyi/input/words.txt")
最小パーティション数が設定されていない場合は、デフォルト値が使用されます。デフォルトのパーティション数は CPU とは関係ありません。ローカル ファイルの場合はファイル サイズに関係し、ファイルの場合はファイル サイズに関係します。 HDFS では、ブロック数に関係します。
方法:
sc.wholeTextFiles(文件路径,最小分区数)
機能: 小さなファイルの束を読み取る
rdd= sc.wholeTextFiles("../data/input/tiny_files")
その使用方法
textFile
は基本的にメソッドと同じです。この API は小さなファイルを読み取るために使用され、フォルダーをパラメーターとして使用してフォルダー内のすべての小さなファイルを読み取ることもできることに注意してください。そのため、パーティションが多すぎると、多くの不要なシャッフルが発生します。したがって、できるだけ少ないパーティションでデータを読み取る必要があり、パーティションの最大数はファイルの数と同じに設定できます。rdd= sc.wholeTextFiles("../data/input/tiny_files/",6) print("当前分区数为",rdd.getNumPartitions())
5 つのファイルが読み取られているため、パーティションを 6 に設定することは無効です。
この API を通じて作成される rdd 形式は次のとおりです。
RDDオペレーター
オペレーター: 分散コレクション オブジェクトの API
RDD 演算子は、Transformation (変換演算子) と Action (アクション演算子) に分かれています。
変換演算子:
戻り値は依然として RDD オブジェクトです。
特徴:オペレーターは遅延ロードされます。アクションオペレーターが存在しない場合、変換オペレーターは機能しません。
アクション演算子:
戻り値は RDD オブジェクトではなくなりました
変換演算子は実行計画の構築に相当し、アクション演算子は実行計画を機能させます。
よく使用される変換演算子
地図
機能: 定義された関数ロジックに従って、RDD 内のデータを 1 つずつ処理します。
例:
rdd.map(lambda x:x*10)
フラットマップ
機能: RDD オブジェクトのマップ操作とネスト解除を実行します。
ネスト解除の意味:
例:
rdd = sc.parallelize(["hadoop spark hadoop", "spark hadoop hadoop", "hadoop flink spark"]) rdd2 = rdd.flatMap(lambda line: line.split(" ")) print(rdd2.collect())
操作結果:
reduceByKey
機能:KV型RDDの場合、キーごとに自動的にグループ化し、集計ロジックに従ってグループ内のデータ(値)の集計を完了します。
例:
rdd = sc.parallelize([('a', 1), ('a', 1), ('b', 1), ('b', 1), ('a', 1)]) # 以key为单位进行计数 print(rdd.reduceByKey(lambda a, b: a + b).collect())
結果:
ReduceByKey は集計操作のみを担当し、グループ化は担当しないことに注意してください。グループ化はキーに従って自動的に完了します。
グループ化
機能:RDDのデータをグループ化する
構文:
rdd.groupBy(func)
; func はグループ化論理関数です例:
rdd = sc.parallelize([('a', 1), ('a', 1), ('b', 1), ('b', 2), ('b', 3)]) # 根据key进行分组 # t代表的是每一个元组,t[0]代表的就是key result = rdd.groupBy(lambda t: t[0]) print(result.collect())
結果:
フィルター
機能: RDD 内のデータをフィルタリングします。
構文:
rdd.filter(func)
; func の戻り値は true または false である必要があります。例:
rdd = sc.parallelize([1, 2, 3, 4, 5, 6]) # 通过Filter算子, 过滤奇数 result = rdd.filter(lambda x: x % 2 == 1) print(result.collect())
結果:
明確な
機能: RDD 内のデータの重複排除
構文:(
rdd.distinct(去重分区数量)
通常、パラメータは空白のままです)例:
rdd = sc.parallelize([1, 1, 1, 2, 2, 2, 3, 3, 3]) print(rdd.distinct().collect())
結果:
連合
機能: 2 つの RDD を 1 つの RDD にマージします。
文法:
rdd.union(other_rdd)
例:
rdd1 = sc.parallelize([1, 1, 3, 3]) rdd2 = sc.parallelize(["a", "b", "a",(1,23),[1,2]]) rdd3 = rdd1.union(rdd2) print(rdd3.collect())
結果:
このことから、異なる種類の rdd を混合できることもわかります。
参加する
機能: 2つのRDDにJOIN操作を実装(SQLによる接続と同様、左外部結合/右外部結合も実現可能)
結合操作はバイナリ グループでのみ機能します
構文:
rdd.join(other_rdd)
;;rdd.leftOuterJoin(other_rdd)
rdd.rightOuterJoin(other_rdd)
例:
rdd1 = sc.parallelize([ (1001, "zhangsan"), (1002, "lisi"), (1003, "wangwu"), (1004, "zhaoliu") ]) rdd2 = sc.parallelize([ (1001, "销售部"), (1002, "科技部")]) # 连接 print(rdd1.join(rdd2).collect()) # 左外连接 print(rdd1.leftOuterJoin(rdd2).collect())
結果:
交差点
機能: 2 つの rdd の交点を見つけます。
文法:
rdd.intersection(other_rdd)
例:
rdd1 = sc.parallelize([('a', 1), ('a', 3)]) rdd2 = sc.parallelize([('a', 1), ('b', 3)]) # 求交集 rdd3 = rdd1.intersection(rdd2) print(rdd3.collect())
結果:
輝き
機能: rdd のデータをネストします; ネストはパーティションに従って実行されます
文法:
rdd.glom()
例:
rdd = sc.parallelize([1, 2, 3, 4, 5, 6, 7, 8, 9], 2) print(rdd.glom().collect())
結果:
ネストを解除したい場合は、次のようにしてください
flatMap
。rdd = sc.parallelize([1, 2, 3, 4, 5, 6, 7, 8, 9], 2) print(rdd.glom().flatMap(lambda x:x).collect())
結果:
groupByKey
機能:KVタイプrddの場合、キーごとにグループ化
reduceByKey と比較して集約された操作はありません
文法:
rdd.groupByKey(func)
例:
rdd = sc.parallelize([('a', 1), ('a', 1), ('b', 1), ('b', 1), ('b', 1)]) rdd2 = rdd.groupByKey() print(rdd2.map(lambda x: (x[0], list(x[1]))).collect())
結果:
並べ替え
機能: rdd 内のデータをソートします。
文法:
rdd.sortBy(func,ascending=False,numPartitions=1)
ascending: 昇順の場合は True、降順の場合は False
numPartitions: 並べ替えに使用されるパーティションの数。グローバルな順序付けが必要な場合は、並べ替えパーティションの数を 1 に設定する必要があります。
例:
rdd = sc.parallelize([('c', 3), ('f', 1), ('b', 11), ('c', 3), ('a', 1), ('c', 5), ('e', 1), ('n', 9), ('a', 1)], 3) # 按照key来进行全局降序排序 print(rdd.sortBy(lambda x: x[0], ascending=False, numPartitions=1).collect())
結果:
キーによるソート
機能: KV タイプ rdd の場合、キーに従ってソートします。
文法:
rdd.sortByKey(ascending=False,numPartitions=1,keyfunc=<function RDD.lambda>)
keyfunc はソート前のキーの前処理操作であり、残りのパラメータは sortBy と同じです。
例:
rdd = sc.parallelize([('a', 1), ('E', 1), ('C', 1), ('D', 1), ('b', 1), ('g', 1), ('f', 1), ('y', 1), ('u', 1), ('i', 1), ('o', 1), ('p', 1), ('m', 1), ('n', 1), ('j', 1), ('k', 1), ('l', 1)], 3) # 先将key全部转化为小写字母,然后根据key值进行全局升序排序 print(rdd.sortByKey(ascending=True, numPartitions=1, keyfunc=lambda key: str(key).lower()).collect())
結果:
ここで注意すべき点は、sorted rdd では、前処理によってキーの値が変化しないことです。
マップ値
機能: バイナリ グループ RDD の場合、その値に対してマップ操作を実行します。
文法:
rdd.mapValues(func)
例:
もちろん、マップを使用して実装することもできます。
よく使用されるアクション演算子
countByKey
機能: キーの出現数をカウントします。通常、KV タイプ rdd に使用されます。
文法:
rdd.countByKey()
集める
機能: rdd の各パーティションのデータをドライバーに収集して、List オブジェクトを形成します。
文法:
rdd.collect()
減らす
機能: 入力ロジックに従ってrdd内のデータを集計します。
文法:
rdd.reduce(func)
折り畳み
機能:reduce と同様に、rdd 内のデータを集計しますが、集計には初期値があります。
文法:
rdd.fold(value,func)
注: 各パーティションを集約する場合は初期値が必要であり、パーティションを集約する場合は初期値も必要です。
例:
rdd = sc.parallelize([1, 2, 3, 4, 5, 6, 7, 8, 9], 3) print(rdd.fold(10, lambda a, b: a + b))
結果: 85
初期値は 10 で、3 つのパーティション内の集計は +10 にする必要があり、パーティション間の集計は +10 になります。
初め
機能: rdd の最初の要素を取り出す
文法:
rdd.first()
取る
機能: rdd の最初の N 要素を取り出し、リストを形成して返します。
文法:
rdd.take(N)
上
機能: rdd 内のデータを降順に並べ替え、最初の N を取り出してリストを形成し、返します。
文法:
rdd.top(N)
注: 先頭の並べ替えはパーティションの影響を受けません。
例:
sc.parallelize([1,2,3,4,5,6,7],2).top(5)
結果:
カウント
機能: rdd にデータがいくつあるかを数える
文法:
rdd.count()
サンプルを取る
機能: RDD からデータをランダムにサンプリングします。
文法:
rdd.takeSample(是否重复,采样数,随机数种子)
繰り返すかどうか: True は繰り返しを許可、False は繰り返しを許可しません。ここでの繰り返しとは、同じ位置のデータを取得することを指し、データの内容とは関係ありません。
サンプル数: 合計で取得されるデータの量
乱数シード: 任意の数値で十分です。このパラメータは通常は入力されません。2 つのサンプリングで同じ数値が入力された場合、2 つのサンプリングの結果は同じになります。
例:
rdd = sc.parallelize([1, 3, 5, 3, 1, 3, 2, 6, 7, 8, 6], 1) print(rdd.takeSample(False, 5, 1))
結果:
2 回実行して、サンプルが同一であることを確認します
取る注文した
関数: RDD をソートし、最初の N を取得します
文法:
rdd.takeOrdered(N,func)
func のラムダ式を使用すると、昇順/降順の操作を実現できます。top と比較して、昇順での並べ替えだけでなく、
デフォルトは昇順ソートです
例:
rdd = sc.parallelize([1, 3, 2, 4, 7, 9, 6], 1) # 升序 print(rdd.takeOrdered(3)) # 降序 print(rdd.takeOrdered(3, lambda x: -x))
結果:
ラムダ式の関数は並べ替え前にデータを処理しますが、元のデータには影響を与えないことがわかります。
フォーリーチ
機能: rdd 内のデータに対して入力論理演算を実行します (戻り値がないことを除いて、map に似ています)
文法:
rdd.foreach(func)
例:
rdd = sc.parallelize([1, 3, 2, 4, 7, 9, 6], 1) result = rdd.foreach(lambda x: print(x * 10))
結果:
ドライバーを経由せずにエグゼキューターによって直接出力を印刷します。
テキストファイルとして保存
機能: rdd データをテキスト ファイル (ローカルまたは hdfs) に書き込みます
文法:
rdd.saveAsTextFile(filePath)
例:
rdd = sc.parallelize([1, 3, 2, 4, 7, 9, 6], 3) rdd.saveAsTextFile("hdfs://10.245.150.47:8020/user/wuhaoyi/output/out1")
結果:
ファイルを保存する場合、ドライバーを介さずに分散して実行されるため、各パーティションに保存されたファイルが生成されます。
保存されたファイルとその検証ファイルはフォルダーに保存されますが、このフォルダーは事前に作成できないことに注意してください。
パーティション演算子
マップパーティション(変換)
機能:パーティション単位のマップ操作
文法:
rdd.mapPartitions(func)
例:
rdd = sc.parallelize([1, 3, 2, 4, 7, 9, 6], 3) def process(iter): result = [] for it in iter: result.append(it * 10) return result print(rdd.mapPartitions(process).collect())
結果:
foreachPartition(アクション)
機能: foreach と同様に、一度にパーティション全体のデータを処理します。
文法:
rdd.foreachPartition(func)
例:
注: このメソッドには固定された出力順序はありません。最初に処理されたパーティションが最初に出力されます。
パーティションによる(変換)
機能: RDD でカスタム パーティション操作を実行します。
文法:
rdd.partitionBy(partitionNum,func)
パラメータ 1: カスタム パーティションの数、パラメータ 2: カスタム パーティション ルール
例:
rdd = sc.parallelize([('hadoop', 1), ('spark', 1), ('hello', 1), ('flink', 1), ('hadoop', 1), ('spark', 1)]) # 使用partitionBy 自定义 分区 def process(k): if 'hadoop' == k or 'hello' == k: return 0 if 'spark' == k: return 1 return 2 print(rdd.partitionBy(3, process).glom().collect())
結果:
修復/合体
機能: RDD のデータを再分割する
構文:
rdd.reparation(N)
/rdd.coalesce(N,是否允许shuffle)
合体では、パラメータ 2 はパーティショニングが許可されるかどうかを示します。True はシャッフルが許可され、パーティションを追加できることを意味し、False はシャッフルが許可されず、パーティションを追加できないことを意味します。
Spark では、グローバル ソートを 1 パーティションに設定する必要があることを除いて、パーティション関連の API は通常、他の場合には無視されます。
したがって、補償による再パーティション化は並列計算に影響を与え、パーティションが増加するとシャッフルが発生する可能性があるため推奨されません。
例:
rdd = sc.parallelize([1, 2, 3, 4, 5], 3) # repartition 修改分区 print(rdd.repartition(1).getNumPartitions()) print(rdd.repartition(5).getNumPartitions()) # coalesce 修改分区(这里因为shuffle为False,所以不能增加分区,但可以减少分区) print(rdd.coalesce(1).getNumPartitions()) print(rdd.coalesce(5, shuffle=True).getNumPartitions())
結果:
修復と比較して、通常は合体を使用することをお勧めします