spark_first_steps

Transcription

spark_first_steps
spark_first_steps
February 12, 2017
1
Premiers pas avec Spark
In [ ]: %matplotlib inline
In [ ]: from jyquickhelper import add_notebook_menu
add_notebook_menu()
Out[ ]: <IPython.core.display.HTML object>
1.1
Deux ou trois petites choses à ne pas oublier
1.1.1
Local et cluster
Spark n’est pas un langage de programmation mais un environnement de calcul distribué.
L’installation en locale reproduit ce que Spark donnerait à grande échelle sur un cluster mais
ce n’est pas rigoureusement identique. En particulier cela veut dire que si votre script tourne en
local sur un petit jeu de données, il est possible qu’il échoue sur le cluster :
• Les dépendances du script sont installées en local mais pas sur chaque machine du cluster
Spark. Cela peut se faire à l’installation du cluster pour des dépendances conséquentes ou
juste avant l’exécution d’un job pour des dépendances ponctuelles.
• Les données sur le cluster sont en plus grand nombre, il est fort probable que l’échantillon
aléatoire local ne soit pas représentatif.
• Les chemins locaux ne fonctionnent pas sur le cluster. Il faudra d’abord uploader les données
sur le cluster pour faire tourner le script.
• Débugger est compliqué : les print ne marchent pas souvent, surtout si c’est en distribué. Le
print va s’exécuter sur une machine distance qui est à mille lieues de votre écran.
Quand ça plante sur une machine distante, il faut s’accrocher. Le pire, c’est quand l’erreur arrive
pour une observation toute bizarre après cinq heures de calcul. Si le message d’erreur n’est pas
trop incompréhensible, on sen tire. En fait, le plus agaçant, c’est quand le calcul est carrément
interrompu par le cluster au bout de cinq heures car il décrète que les probabilités d’aboutir sont
quasi nulles. Là, on connaît l’erreur (skewed dataset) et on sait qu’on va souffrir pour construire
la contournante.
1
1.1.2
Spark et RDD
Spark ne manipule pas des fichiers mais des Resilient Distributed Dataset ou RDD. En particulier
:
1. Les RDD sont organisés en ligne : ce sont des blocs qui ne seront jamais cassés ni modifié. Ces
lignes ne peuvent pas excéder 2 Go (voir SPARK-6235) mais il est conseillé de ne pas aller
au-delà de quelques Mo.
2. Sauf exception, il est impossible d’accéder à une partie du fichier. Il faut le parcourir en
entier (il n’y a pas d’index).
3. Les RDD fonctionnent comme des flux ou steam. On peut soit les lire, soit les écrire mais
jamais les deux en même temps. Par conséquent, on ne peut pas modifier un RDD, il faut
toujours en créer un autre.
4. Les RDD sont distribués. L’ordre des lignes qui le composent n’est pas prévisible.
5. Comme l’ordre est imprévisible, on ne stocke jamais les noms des colonnes dans les RDD.
1.1.3
Les partitions
Il existe une exception au point 2 : les partitions. Une partition est un ensemble de lignes traitées
par le même processus. La parallélisation ne peut excéder le nombre de partitions. Par défaut,
c’est aléatoire (hash hash). Mais on peut tout-à-fait partionner selon une colonne, deux colonnes.
D’ailleurs, c’est là-dessus qu’on joue pour optimiser la distribution. Si on réduit (ou grouper)
selon une colonne, c’est d’autant plus rapide si le stream est déjà partitionnée sur cette colonne.
1.1.4
Spark et Python
Spark est implémenté en Java. L’API Python permet de faire beaucoup de choses mais :
• Elle ne sera jamais aussi complète que l’API Java.
• Elle sera plus lente que l’API Java ou Scala (car Scala est une surcouche fonctionnelle de
Java).
1.1.5
Librairies sur Spark
Un des succès de Spark est de proposer des extensions dédiées à certains usages comme MLlib
qui implémente des algorihmes de machine learning distribués, GraphX pour des algorithmes sur
des graphes. MLlib sera bientôt remplacé par ML qui s’appuie sur les DataFrame.
1.1.6
Erreur : Cannot run program "python"
Il vous manque probablement PYSPARK_PYTHON.
In [ ]: import os
for o, v in sorted(os.environ.items()):
if "SPARK" in o.upper():
print("{0:25}= {1}".format(o, v.replace(os.environ["USERNAME"], "<username>")))
LOCAL_PYSPARK
PYSPARK_DRIVER_PYTHON
PYSPARK_PYTHON
= c:\<username>\spark\spark-2.0.2-bin-hadoop2.7
= jupyter-notebook
= c:\Python35_x64\python
2
PYSPARK_SUBMIT_ARGS
SPARK_CMD
SPARK_ENV_LOADED
SPARK_HIVE
SPARK_HOME
SPARK_JARS_DIR
SPARK_SCALA_VERSION
_SPARK_CMD_USAGE
1.1.7
=
=
=
=
=
=
=
=
"--name" "PySparkShell" "pyspark-shell"
set PYSPARK_SUBMIT_ARGS="--name" "PySparkShell" "pyspark-shell" && ju
1
true
c:\<username>\spark\spark-2.0.2-bin-hadoop2.7\bin\..
"c:\<username>\spark\spark-2.0.2-bin-hadoop2.7\bin\..\jars"
2.10
Usage: bin\pyspark.cmd [options]
Erreur : Output directory file:/... already exists
Spark n’aime pas écrire des données dans un RDD qui existe déjà. Il faut le supprimer. Tout
dépend de l’environnement où on se trouve, sur Hadoop ou en local. Comme c’est en local, nous
ferons :
In [ ]: from pyquickhelper.filehelper import remove_folder
def clean(folder):
if os.path.exists(folder):
return remove_folder(folder)
else:
return []
clean("fichier.out.txt")
Out[ ]: [('fichier.out.txt\\.part-00000.crc', 'file'),
('fichier.out.txt\\.part-00001.crc', 'file'),
('fichier.out.txt\\._SUCCESS.crc', 'file'),
('fichier.out.txt\\part-00000', 'file'),
('fichier.out.txt\\part-00001', 'file'),
('fichier.out.txt\\_SUCCESS', 'file'),
('fichier.out.txt', 'dir')]
1.1.8
Vérifier que Spark en local fonctionne
On essaye le "hello world" en Spark qui consiste à compter les mots dans un fichier. On prend le
fichier du notebook.
In [ ]: text_file = sc.textFile("spark_first_steps.ipynb")
counts = text_file.flatMap(lambda line: line.split(" ")).map(lambda word: (word, 1)).red
counts.saveAsTextFile("fichier.out.txt")
In [ ]: os.listdir("fichier.out.txt/")
Out[ ]: ['.part-00000.crc',
'.part-00001.crc',
'._SUCCESS.crc',
'part-00000',
'part-00001',
'_SUCCESS']
3
1.1.9
Sortie en plusieurs fichiers
Un job Spark est distribué. La sortie d’un job Spark s’effectue sous la forme de plusieurs stream
dans un répertoire, un stream par processus. Cela explique la présence de part-00000, part-00001.
Le fichier _SUCCESS indique le statut du job.
In [ ]: %load_ext pyensae
%head fichier.out.txt/part-00000 -n 3
Out[ ]: <IPython.core.display.HTML object>
Le format dépend du dernier résultat.
1.2
Les opérations de bases
Documentation : programming-guide.html - transformations.
Dans cette section, on considère les données comme un ensemble de lignes de texte. Rien de plus.
Donc, pas d’information de type, des conversions quasiment tout le temps. Bref, c’est utile pour
comprendre. On y revient quand le reste ne marche pas. En général, on commence par Spark SQL.
Ah oui j’oubliais, on s’en sert beaucoup quand les données ne sont pas structurées et sont décrites
par du JSON, genre des logs d’un site internet. Chaque ligne est en fait un gros JSON.
On utilise un jeu de données de machine learning Adult légèrement pré-traités et que vous pourrez trouver sur GitHub : td3a_spark.
In [ ]: import os
if not os.path.exists("data_adult.txt"):
from pyquickhelper.filehelper import unzip_files
unzip_files("data_adult.zip", where_to=".")
assert os.path.exists("data_adult.txt")
In [ ]: import pandas
df = pandas.read_csv("data_adult.txt", sep="\t", encoding="utf-8")
df.head()
Out[ ]:
0
1
2
3
4
0
1
2
3
4
age
39
50
38
53
28
workclass
State-gov
Self-emp-not-inc
Private
Private
Private
marital_status
Never-married
Married-civ-spouse
Divorced
Married-civ-spouse
Married-civ-spouse
fnlwgt
77516
83311
215646
234721
338409
education education_num \
Bachelors
13
Bachelors
13
HS-grad
9
11th
7
Bachelors
13
occupation
Adm-clerical
Exec-managerial
Handlers-cleaners
Handlers-cleaners
Prof-specialty
relationship
Not-in-family
Husband
Not-in-family
Husband
Wife
race
White
White
White
Black
Black
capital_gain capital_loss hours_per_week native_country target
4
sex \
Male
Male
Male
Male
Female
0
1
2
3
4
2174
0
0
0
0
0
0
0
0
0
40
13
40
40
40
United-States
United-States
United-States
United-States
Cuba
<=50K
<=50K
<=50K
<=50K
<=50K
On enlève le nom des colonnes.
In [ ]: df.to_csv("adult.txt", sep="\t", encoding="utf-8", index=False, header=None)
In [ ]: %head adult.txt -n 2
Out[ ]: <IPython.core.display.HTML object>
1.2.1
déclaration d’un RDD
La déclaration déclare l’existence d’un RDD comme on déclare un fichier. Pour l’instant aucune
manipulation.
In [ ]: rdd = sc.textFile("adult.txt")
1.2.2
enregistrement d’un RDD
In [ ]: import os
if not os.path.exists("out"):
os.mkdir("out")
In [ ]: clean("out/copy_adult.txt")
rdd.saveAsTextFile(os.path.abspath("out/copy_adult.txt"))
In [ ]: %head out/copy_adult.txt/part-00000 -n 2
Out[ ]: <IPython.core.display.HTML object>
1.2.3
lecture locale d’un RDD avec pandas
On lit chaque morceaux avant de les concaténer.
In [ ]: import glob
import pandas
def read_rdd(path, **options):
pat = os.path.join(path, "part*")
all_files = glob.glob(pat)
if len(all_files) == 0:
raise Exception("No file to read in '{0}'".format(path))
merge = []
for f in all_files:
try:
df = pandas.read_csv(f, header=None, **options)
except Exception as e:
5
raise Exception("Unable to read '{0}'".format(f)) from e
merge.append(df)
if len(merge) == 0:
raise Exception("No file to read in '{0}'".format(path))
concatenated_df = pandas.concat(merge, ignore_index=True)
return concatenated_df
data = read_rdd("out/copy_adult.txt", sep="\t", encoding="utf-8")
data.head(n=2)
Out[ ]:
1.2.4
0
0 39
1 50
1
2
State-gov 77516
Self-emp-not-inc 83311
0
1
6
Adm-clerical
Exec-managerial
0
1
13
United-States
United-States
3 4
Bachelors 13
Bachelors 13
7
Not-in-family
Husband
8
White
White
5 \
Never-married
Married-civ-spouse
9
Male
Male
10
2174
0
11 12 \
0 40
0 13
14
<=50K
<=50K
collect
Cette opération regroupe les deux précédentes en une seule. Il faut toute de même faire attention
de ne pas l’exécuter sur un grand fichier sous peine de faire exploser la mémoire.
In [ ]: res = rdd.collect()
In [ ]: res[:2]
Out[ ]: ['39\t State-gov\t77516\t Bachelors\t13\t Never-married\t Adm-clerical\t Not-in-family\t
'50\t Self-emp-not-inc\t83311\t Bachelors\t13\t Married-civ-spouse\t Exec-managerial\t
In [ ]: import pandas
df = pandas.DataFrame([_.split("\t") for _ in res])
df.head(2)
Out[ ]:
0
0 39
1 50
1
2
State-gov 77516
Self-emp-not-inc 83311
0
1
6
Adm-clerical
Exec-managerial
0
1
13
United-States
United-States
3 4
Bachelors 13
Bachelors 13
7
Not-in-family
Husband
14
<=50K
<=50K
6
8
White
White
5 \
Never-married
Married-civ-spouse
9
Male
Male
10 11 12 \
2174 0 40
0 0 13
1.2.5
map
Transformer une ligne en une autre ligne. Chaque ligne est traitée indépendemment des autres.
In [ ]: def extract_column(cols, row):
spl = row.split("\t")
return [spl[i].strip() for i in cols]
res = rdd.map(lambda row: extract_column([1,3], row))
res.collect()[:2]
Out[ ]: [['State-gov', 'Bachelors'], ['Self-emp-not-inc', 'Bachelors']]
1.2.6
filter
Garder ou jeter une ligne. Chaque ligne est traitée indépendemment des autres.
In [ ]: def filter_column(row):
spl = row.split("\t")
return spl[-1].strip() != "<=50K"
res = rdd.filter(lambda row: filter_column(row))
res.collect()[:2]
Out[ ]: ['52\t Self-emp-not-inc\t209642\t HS-grad\t9\t Married-civ-spouse\t Exec-managerial\t Hu
'31\t Private\t45781\t Masters\t14\t Never-married\t Prof-specialty\t Not-in-family\t W
On combine souvent les deux :
In [ ]: def filter_column_split(row):
return row[-1].strip() != "<=50K"
res = rdd.map(lambda row: extract_column([1,3,-1], row)) \
.filter(lambda row: filter_column_split(row))
res.collect()[:2]
Out[ ]: [['Self-emp-not-inc', 'HS-grad', '>50K'], ['Private', 'Masters', '>50K']]
Il faut faire attention aux transformations successives des lignes.
1.2.7
flatMap
C’est la principale différence avec SQL. Une ligne peut devenir un nombre variable de lignes.
In [ ]: def extract_column_and_multiply_row(n, row):
spl = row.split("\t")
return [tuple(_.strip() for _ in spl)] * n
res = rdd.flatMap(lambda row: extract_column_and_multiply_row(2, row))
res.collect()[:3]
7
Out[ ]: [('39',
'State-gov',
'77516',
'Bachelors',
'13',
'Never-married',
'Adm-clerical',
'Not-in-family',
'White',
'Male',
'2174',
'0',
'40',
'United-States',
'<=50K'),
('39',
'State-gov',
'77516',
'Bachelors',
'13',
'Never-married',
'Adm-clerical',
'Not-in-family',
'White',
'Male',
'2174',
'0',
'40',
'United-States',
'<=50K'),
('50',
'Self-emp-not-inc',
'83311',
'Bachelors',
'13',
'Married-civ-spouse',
'Exec-managerial',
'Husband',
'White',
'Male',
'0',
'0',
'13',
'United-States',
'<=50K')]
8
1.2.8
group / reduce + mapValues
Petite moyenne ?
In [ ]: def extract_age_rich(row):
spl = row.split("\t")
target = spl[-1].strip()
age = float(spl[0])
return (age, target)
def custom_agg(aggset):
temp = list([_[0] for _ in aggset])
return len(temp), sum(temp)
ave = rdd.map(extract_age_rich).groupBy(lambda row: row[1]).mapValues(custom_agg)
fin = ave.collect()
fin
Out[ ]: [('>50K', (7841, 346963.0)), ('<=50K', (24720, 909294.0))]
1.2.9
sort
Je n’en parle pas. Trier un gros jeu de données est à proscrire. On peut trier au sein d’un groupe
mais jamais un stream entier. Ca fait presque dix ans que j’écris des jobs map/reduce, je n’ai
jamais écrit un sort sur tout un jeu de données. Ca s’appelle flinguer de la CPU pour rien.
1.2.10
join
Et on remet la moyenne dans le stream initial. Il vaut mieux regarder la documentation de la
méthode join avant de commencer à lire le code qui suit.
In [ ]: add_key = rdd.map(lambda row: row.split("\t")).map(lambda row: (row[-1].strip(), row))
join = add_key.join(ave)
join.collect()[:2]
Out[ ]: [('>50K',
(['52',
' Self-emp-not-inc',
'209642',
' HS-grad',
'9',
' Married-civ-spouse',
' Exec-managerial',
' Husband',
' White',
' Male',
'0',
'0',
'45',
9
' United-States',
' >50K'],
(7841, 346963.0))),
('>50K',
(['31',
' Private',
'45781',
' Masters',
'14',
' Never-married',
' Prof-specialty',
' Not-in-family',
' White',
' Female',
'14084',
'0',
'50',
' United-States',
' >50K'],
(7841, 346963.0)))]
On commence à comprendre pourquoi Spark SQL, ça risque d’être pas mal.
1.2.11
le choix existentiel du join : le petit join
On fait souvent une opération qui consiste à garder les lignes pour lesquelles une certaine valeur
appartient à un ensemble. On peut faire un join classique ou alors l’ensemble est petit, traiter ce
join comme un map. On broadcaste l’ensemble à chaque processus exécutant le map.
In [ ]: from pyspark.context import SparkContext
ages = sc.broadcast([20, 30, 40])
ages.value
Out[ ]: [20, 30, 40]
In [ ]: subset = rdd.filter(lambda row: int(row.split("\t")[0]) in ages.value )
subset.collect()[:2]
Out[ ]: ['30\t State-gov\t141297\t Bachelors\t13\t Married-civ-spouse\t Prof-specialty\t Husband
'40\t Private\t121772\t Assoc-voc\t11\t Married-civ-spouse\t Craft-repair\t Husband\t A
1.2.12
les trucs qui servent parfois parce que ... à l’usage ça sert
Ce que font les méthodes associées aux RDD, un peu comme les itérateurs, n’est pas toujours
intuitif, mais il est à peu près sûr qu’elles vous serviront un jour (peut-être après avoir googlé ou
bingé comme des fous).
In [ ]: simple_rdd = sc.parallelize([2, 3, 4])
simple_rdd.collect()
10
Out[ ]: [2, 3, 4]
In [ ]: simple_rdd.flatMap(lambda x: range(1, x)).collect()
Out[ ]: [1, 1, 2, 1, 2, 3]
histogram, groupByKey
1.2.13
le truc à retenir
collect, collect... qu’est-ce que je voulais dire déjà... Ah oui... Un job map/reduce c’est :
1. La déclaration des flux d’entrées.
2. Le traitement à proprement parler.
3. La déclaration des flux de sorties.
A moins d’écrire du java bas niveau, le job est transformé en un plan d’exécution qui n’est jamais
exécuté si collect ou save machin chouette n’est jamais exécuté. Bref, c’est du lazy.
1.3
Spark DataFrame
Spark SQL
Au début, ça commence par... créer un dataframe. Et comme pour pandas, ces objets retienennt
les noms et les types.
In [ ]: import pandas
data = pandas.read_csv("data_adult.txt", sep="\t", encoding="utf-8")
data.head(2)
Out[ ]:
0
1
0
1
age
39
50
workclass fnlwgt
State-gov
77516
Self-emp-not-inc 83311
marital_status
Never-married
Married-civ-spouse
education education_num \
Bachelors
13
Bachelors
13
occupation
Adm-clerical
Exec-managerial
relationship
Not-in-family
Husband
race
White
White
sex \
Male
Male
capital_gain capital_loss hours_per_week native_country target
0
2174
0
40
United-States <=50K
1
0
0
13 United-States <=50K
In [ ]: if "spark" not in locals():
from pyspark.sql import SparkSession
spark = SparkSession.builder.appName("nimportequoi").getOrCreate()
In [ ]: # sdf = spark.createDataFrame(data) # ça marche
sdf = spark.read.csv("data_adult.txt", sep="\t", encoding="utf-8")
In [ ]: sdf.show()
11
# à ne faire qu'
+---+-----------------+------+-------------+-------------+--------------------+----------------|_c0|
_c1| _c2|
_c3|
_c4|
_c5|
_c
+---+-----------------+------+-------------+-------------+--------------------+----------------|age|
workclass|fnlwgt|
education|education_num|
marital_status|
occupatio
| 39|
State-gov| 77516|
Bachelors|
13|
Never-married|
Adm-clerica
| 50| Self-emp-not-inc| 83311|
Bachelors|
13| Married-civ-spouse| Exec-manageria
| 38|
Private|215646|
HS-grad|
9|
Divorced| Handlers-cleaner
| 53|
Private|234721|
11th|
7| Married-civ-spouse| Handlers-cleaner
| 28|
Private|338409|
Bachelors|
13| Married-civ-spouse|
Prof-specialt
| 37|
Private|284582|
Masters|
14| Married-civ-spouse| Exec-manageria
| 49|
Private|160187|
9th|
5| Married-spouse-a...|
Other-servic
| 52| Self-emp-not-inc|209642|
HS-grad|
9| Married-civ-spouse| Exec-manageria
| 31|
Private| 45781|
Masters|
14|
Never-married|
Prof-specialt
| 42|
Private|159449|
Bachelors|
13| Married-civ-spouse| Exec-manageria
| 37|
Private|280464| Some-college|
10| Married-civ-spouse| Exec-manageria
| 30|
State-gov|141297|
Bachelors|
13| Married-civ-spouse|
Prof-specialt
| 23|
Private|122272|
Bachelors|
13|
Never-married|
Adm-clerica
| 32|
Private|205019| Assoc-acdm|
12|
Never-married|
Sale
| 40|
Private|121772|
Assoc-voc|
11| Married-civ-spouse|
Craft-repai
| 34|
Private|245487|
7th-8th|
4| Married-civ-spouse| Transport-movin
| 25| Self-emp-not-inc|176756|
HS-grad|
9|
Never-married|
Farming-fishin
| 32|
Private|186824|
HS-grad|
9|
Never-married| Machine-op-inspc
| 38|
Private| 28887|
11th|
7| Married-civ-spouse|
Sale
+---+-----------------+------+-------------+-------------+--------------------+----------------only showing top 20 rows
1.3.1
Conversion à pandas
In [ ]: df = sdf.toPandas()
In [ ]: df.head()
Out[ ]:
_c0
0 age
1 39
2 50
3 38
4 53
0
1
2
3
4
_c1
_c2
workclass fnlwgt
State-gov
77516
Self-emp-not-inc 83311
Private 215646
Private 234721
_c5
marital_status
Never-married
Married-civ-spouse
Divorced
Married-civ-spouse
_c3
_c4 \
education education_num
Bachelors
13
Bachelors
13
HS-grad
9
11th
7
_c6
occupation
Adm-clerical
Exec-managerial
Handlers-cleaners
Handlers-cleaners
12
_c7
relationship
Not-in-family
Husband
Not-in-family
Husband
_c8
race
White
White
White
Black
_c9 \
sex
Male
Male
Male
Male
0
1
2
3
4
1.3.2
_c10
capital_gain
2174
0
0
0
_c11
capital_loss
0
0
0
0
_c12
_c13
_c14
hours_per_week native_country target
40
United-States <=50K
13 United-States <=50K
40 United-States <=50K
40 United-States <=50K
Retour aux RDD
In [ ]: sdf.rdd
Out[ ]: MapPartitionsRDD[59] at javaToPython at null:-2
1.3.3
Récuperer le schéma
In [ ]: sdf.schema
Out[ ]: StructType(List(StructField(_c0,StringType,true),StructField(_c1,StringType,true),Struct
In [ ]: sdf.printSchema()
root
|-|-|-|-|-|-|-|-|-|-|-|-|-|-|--
1.3.4
_c0: string (nullable = true)
_c1: string (nullable = true)
_c2: string (nullable = true)
_c3: string (nullable = true)
_c4: string (nullable = true)
_c5: string (nullable = true)
_c6: string (nullable = true)
_c7: string (nullable = true)
_c8: string (nullable = true)
_c9: string (nullable = true)
_c10: string (nullable = true)
_c11: string (nullable = true)
_c12: string (nullable = true)
_c13: string (nullable = true)
_c14: string (nullable = true)
Utiliser pandas pour spécifer le format
On utilise pandas sur une partie du stream.
In [ ]: import pandas
df = pandas.read_csv("data_adult.txt", sep="\t", encoding="utf-8")
df.head(n=2)
13
Out[ ]:
0
1
0
1
age
39
50
workclass fnlwgt
State-gov
77516
Self-emp-not-inc 83311
marital_status
Never-married
Married-civ-spouse
education education_num \
Bachelors
13
Bachelors
13
occupation
Adm-clerical
Exec-managerial
relationship
Not-in-family
Husband
race
White
White
sex \
Male
Male
capital_gain capital_loss hours_per_week native_country target
0
2174
0
40
United-States <=50K
1
0
0
13 United-States <=50K
In [ ]: sdf = spark.createDataFrame(df)
In [ ]: sdf.printSchema()
root
|-|-|-|-|-|-|-|-|-|-|-|-|-|-|--
age: long (nullable = true)
workclass: string (nullable = true)
fnlwgt: long (nullable = true)
education: string (nullable = true)
education_num: long (nullable = true)
marital_status: string (nullable = true)
occupation: string (nullable = true)
relationship: string (nullable = true)
race: string (nullable = true)
sex: string (nullable = true)
capital_gain: long (nullable = true)
capital_loss: long (nullable = true)
hours_per_week: long (nullable = true)
native_country: string (nullable = true)
target: string (nullable = true)
In [ ]: fullsdf = spark.createDataFrame(sdf.rdd, sdf.schema)
In [ ]: fullsdf.printSchema()
root
|-|-|-|-|-|-|-|-|--
age: long (nullable = true)
workclass: string (nullable = true)
fnlwgt: long (nullable = true)
education: string (nullable = true)
education_num: long (nullable = true)
marital_status: string (nullable = true)
occupation: string (nullable = true)
relationship: string (nullable = true)
race: string (nullable = true)
14
|-|-|-|-|-|--
1.3.5
sex: string (nullable = true)
capital_gain: long (nullable = true)
capital_loss: long (nullable = true)
hours_per_week: long (nullable = true)
native_country: string (nullable = true)
target: string (nullable = true)
Enregistrement au format parquet
In [ ]: fullsdf.write.parquet("data_adult.schema.parquet")
1.3.6
Relecture du format parquet
In [ ]: newsdf = spark.read.parquet("data_adult.schema.parquet/")
In [ ]: newsdf.printSchema()
root
|-|-|-|-|-|-|-|-|-|-|-|-|-|-|--
1.3.7
age: long (nullable = true)
workclass: string (nullable = true)
fnlwgt: long (nullable = true)
education: string (nullable = true)
education_num: long (nullable = true)
marital_status: string (nullable = true)
occupation: string (nullable = true)
relationship: string (nullable = true)
race: string (nullable = true)
sex: string (nullable = true)
capital_gain: long (nullable = true)
capital_loss: long (nullable = true)
hours_per_week: long (nullable = true)
native_country: string (nullable = true)
target: string (nullable = true)
Dataframe Spark VS Dataframe pandas
Spark a reproduit la même interface que pandas pour ses dataframes excepté que le résultat n’est
pas calculé tant qu’on ne choisit pas de sauvegarder le résultat.
In [ ]: fifty = fullsdf [fullsdf.age > 50]
In [ ]: fifty.show()
+---+-----------------+------+-------------+-------------+-------------------+-----------------|age|
workclass|fnlwgt|
education|education_num|
marital_status|
occupation
15
+---+-----------------+------+-------------+-------------+-------------------+-----------------| 53|
Private|234721|
11th|
7| Married-civ-spouse| Handlers-cleaners
| 52| Self-emp-not-inc|209642|
HS-grad|
9| Married-civ-spouse| Exec-managerial
| 54|
Private|302146|
HS-grad|
9|
Separated|
Other-service
| 59|
Private|109015|
HS-grad|
9|
Divorced|
Tech-support
| 56|
Local-gov|216851|
Bachelors|
13| Married-civ-spouse|
Tech-support
| 54|
?|180211| Some-college|
10| Married-civ-spouse|
?
| 53| Self-emp-not-inc| 88506|
Bachelors|
13| Married-civ-spouse|
Prof-specialty
| 57|
Federal-gov|337895|
Bachelors|
13| Married-civ-spouse|
Prof-specialty
| 53|
Private|144361|
HS-grad|
9| Married-civ-spouse| Machine-op-inspct
| 53|
Private|169846|
HS-grad|
9| Married-civ-spouse|
Adm-clerical
| 79|
Private|124744| Some-college|
10| Married-civ-spouse|
Prof-specialty
| 67|
?|212759|
10th|
6| Married-civ-spouse|
?
| 52|
Private|276515|
Bachelors|
13| Married-civ-spouse|
Other-service
| 59|
Private|159937|
HS-grad|
9| Married-civ-spouse|
Sales
| 53|
Private|346253|
HS-grad|
9|
Divorced|
Sales
| 57|
Private|249977|
Assoc-voc|
11| Married-civ-spouse|
Prof-specialty
| 76|
Private|124191|
Masters|
14| Married-civ-spouse| Exec-managerial
| 56| Self-emp-not-inc|335605|
HS-grad|
9| Married-civ-spouse|
Other-service
| 53|
Private| 95647|
9th|
5| Married-civ-spouse| Handlers-cleaners
| 56|
Self-emp-inc|303090| Some-college|
10| Married-civ-spouse|
Sales
+---+-----------------+------+-------------+-------------+-------------------+-----------------only showing top 20 rows
In [ ]:
In [ ]:
16

Documents pareils