import mermaid from 'https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.2.3/mermaid.esm.min.mjs'; mermaid.initialize({ startOnLoad: true });
%matplotlib inline
from jyquickhelper import add_notebook_menu
add_notebook_menu()
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 :
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.
Spark ne manipule pas des fichiers mais des Resilient Distributed Dataset ou RDD. En particulier :
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.
Il vous manque probablement PYSPARK_PYTHON
.
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 = c:\<username>rdupre\spark-2.2.0-bin-hadoop2.7 PYSPARK_DRIVER_PYTHON = jupyter-notebook PYSPARK_PYTHON = c:\Python36_x64\python PYSPARK_SUBMIT_ARGS = "--name" "PySparkShell" "pyspark-shell" SPARK_CMD = set PYSPARK_SUBMIT_ARGS="--name" "PySparkShell" "pyspark-shell" && jupyter-notebook SPARK_ENV_LOADED = 1 SPARK_HIVE = true SPARK_HOME = c:\<username>rdupre\spark-2.2.0-bin-hadoop2.7\bin\.. SPARK_JARS_DIR = "c:\<username>rdupre\spark-2.2.0-bin-hadoop2.7\bin\..\jars" SPARK_SCALA_VERSION = 2.10 _SPARK_CMD_USAGE = Usage: bin\pyspark.cmd [options]
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 :
from pyquickhelper.filehelper import remove_folder
def clean(folder):
if os.path.exists(folder):
return remove_folder(folder)
else:
return []
clean("fichier.out.txt")
[]
On essaye le "hello world" en Spark qui consiste à compter les mots dans un fichier. On prend le fichier du notebook.
text_file = sc.textFile("spark_first_steps.ipynb")
counts = text_file.flatMap(lambda line: line.split(" ")).map(lambda word: (word, 1)).reduceByKey(lambda a, b: a + b)
counts.saveAsTextFile("fichier.out.txt")
os.listdir("fichier.out.txt/")
['.part-00000.crc', '.part-00001.crc', '._SUCCESS.crc', 'part-00000', 'part-00001', '_SUCCESS']
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.
%load_ext pyensae
%head fichier.out.txt/part-00000 -n 3
('', 11686) ('[collect](http://spark.apache.org/docs/latest/api/python/pyspark.html#pyspark.RDD.collect)', 1) ('SQL](http://spark.apache.org/docs/latest/sql-programming-guide.html)\\n",', 1)
Le format dépend du dernier résultat.
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.
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")
import pandas
df = pandas.read_csv("data_adult.txt", sep="\t", encoding="utf-8")
df.head()
age | workclass | fnlwgt | education | education_num | marital_status | occupation | relationship | race | sex | capital_gain | capital_loss | hours_per_week | native_country | target | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | Not-in-family | White | Male | 2174 | 0 | 40 | United-States | <=50K |
1 | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Husband | White | Male | 0 | 0 | 13 | United-States | <=50K |
2 | 38 | Private | 215646 | HS-grad | 9 | Divorced | Handlers-cleaners | Not-in-family | White | Male | 0 | 0 | 40 | United-States | <=50K |
3 | 53 | Private | 234721 | 11th | 7 | Married-civ-spouse | Handlers-cleaners | Husband | Black | Male | 0 | 0 | 40 | United-States | <=50K |
4 | 28 | Private | 338409 | Bachelors | 13 | Married-civ-spouse | Prof-specialty | Wife | Black | Female | 0 | 0 | 40 | Cuba | <=50K |
On enlève le nom des colonnes.
df.to_csv("adult.txt", sep="\t", encoding="utf-8", index=False, header=None)
%head adult.txt -n 2
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
La déclaration déclare l'existence d'un RDD comme on déclare un fichier. Pour l'instant aucune manipulation.
rdd = sc.textFile("adult.txt")
import os
if not os.path.exists("out"):
os.mkdir("out")
clean("out/copy_adult.txt")
rdd.saveAsTextFile(os.path.abspath("out/copy_adult.txt"))
%head out/copy_adult.txt/part-00000 -n 2
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
On lit chaque morceaux avant de les concaténer.
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:
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)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | Not-in-family | White | Male | 2174 | 0 | 40 | United-States | <=50K |
1 | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Husband | White | Male | 0 | 0 | 13 | United-States | <=50K |
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.
res = rdd.collect()
res[:2]
['39\t State-gov\t77516\t Bachelors\t13\t Never-married\t Adm-clerical\t Not-in-family\t White\t Male\t2174\t0\t40\t United-States\t <=50K', '50\t Self-emp-not-inc\t83311\t Bachelors\t13\t Married-civ-spouse\t Exec-managerial\t Husband\t White\t Male\t0\t0\t13\t United-States\t <=50K']
import pandas
df = pandas.DataFrame([_.split("\t") for _ in res])
df.head(2)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | Not-in-family | White | Male | 2174 | 0 | 40 | United-States | <=50K |
1 | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Husband | White | Male | 0 | 0 | 13 | United-States | <=50K |
Transformer une ligne en une autre ligne. Chaque ligne est traitée indépendemment des autres.
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]
[['State-gov', 'Bachelors'], ['Self-emp-not-inc', 'Bachelors']]
Garder ou jeter une ligne. Chaque ligne est traitée indépendemment des autres.
def filter_column(row):
spl = row.split("\t")
return spl[-1].strip() != "<=50K"
res = rdd.filter(lambda row: filter_column(row))
res.collect()[:2]
['52\t Self-emp-not-inc\t209642\t HS-grad\t9\t Married-civ-spouse\t Exec-managerial\t Husband\t White\t Male\t0\t0\t45\t United-States\t >50K', '31\t Private\t45781\t Masters\t14\t Never-married\t Prof-specialty\t Not-in-family\t White\t Female\t14084\t0\t50\t United-States\t >50K']
On combine souvent les deux :
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]
[['Self-emp-not-inc', 'HS-grad', '>50K'], ['Private', 'Masters', '>50K']]
Il faut faire attention aux transformations successives des lignes.
C'est la principale différence avec SQL. Une ligne peut devenir un nombre variable de lignes.
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]
[('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')]
Petite moyenne ?
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
[('>50K', (7841, 346963.0)), ('<=50K', (24720, 909294.0))]
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.
add_key = rdd.map(lambda row: row.split("\t")).map(lambda row: (row[-1].strip(), row))
join = add_key.join(ave)
join.collect()[:2]
[('>50K', (['52', ' Self-emp-not-inc', '209642', ' HS-grad', '9', ' Married-civ-spouse', ' Exec-managerial', ' Husband', ' White', ' Male', '0', '0', '45', ' 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.
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.
from pyspark.context import SparkContext
ages = sc.broadcast([20, 30, 40])
ages.value
[20, 30, 40]
subset = rdd.filter(lambda row: int(row.split("\t")[0]) in ages.value )
subset.collect()[:2]
['30\t State-gov\t141297\t Bachelors\t13\t Married-civ-spouse\t Prof-specialty\t Husband\t Asian-Pac-Islander\t Male\t0\t0\t40\t India\t >50K', '40\t Private\t121772\t Assoc-voc\t11\t Married-civ-spouse\t Craft-repair\t Husband\t Asian-Pac-Islander\t Male\t0\t0\t40\t ?\t >50K']
simple_rdd = sc.parallelize([2, 3, 4])
simple_rdd.collect()
[2, 3, 4]
simple_rdd.flatMap(lambda x: range(1, x)).collect()
[1, 1, 2, 1, 2, 3]
collect, collect... qu'est-ce que je voulais dire déjà... Ah oui... Un job map/reduce c'est :
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.
import pandas
data = pandas.read_csv("data_adult.txt", sep="\t", encoding="utf-8")
data.head(2)
age | workclass | fnlwgt | education | education_num | marital_status | occupation | relationship | race | sex | capital_gain | capital_loss | hours_per_week | native_country | target | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | Not-in-family | White | Male | 2174 | 0 | 40 | United-States | <=50K |
1 | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Husband | White | Male | 0 | 0 | 13 | United-States | <=50K |
if "spark" not in locals():
from pyspark.sql import SparkSession
spark = SparkSession.builder.appName("nimportequoi").getOrCreate() # à ne faire qu'une fois
# sdf = spark.createDataFrame(data) # ça marche
sdf = spark.read.csv("data_adult.txt", sep="\t", encoding="utf-8")
sdf.show()
+---+-----------------+------+-------------+-------------+--------------------+------------------+--------------+-------------------+-------+------------+------------+--------------+--------------+------+ |_c0| _c1| _c2| _c3| _c4| _c5| _c6| _c7| _c8| _c9| _c10| _c11| _c12| _c13| _c14| +---+-----------------+------+-------------+-------------+--------------------+------------------+--------------+-------------------+-------+------------+------------+--------------+--------------+------+ |age| workclass|fnlwgt| education|education_num| marital_status| occupation| relationship| race| sex|capital_gain|capital_loss|hours_per_week|native_country|target| | 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| | 38| Private|215646| HS-grad| 9| Divorced| Handlers-cleaners| Not-in-family| White| Male| 0| 0| 40| United-States| <=50K| | 53| Private|234721| 11th| 7| Married-civ-spouse| Handlers-cleaners| Husband| Black| Male| 0| 0| 40| United-States| <=50K| | 28| Private|338409| Bachelors| 13| Married-civ-spouse| Prof-specialty| Wife| Black| Female| 0| 0| 40| Cuba| <=50K| | 37| Private|284582| Masters| 14| Married-civ-spouse| Exec-managerial| Wife| White| Female| 0| 0| 40| United-States| <=50K| | 49| Private|160187| 9th| 5| Married-spouse-a...| Other-service| Not-in-family| Black| Female| 0| 0| 16| Jamaica| <=50K| | 52| Self-emp-not-inc|209642| HS-grad| 9| Married-civ-spouse| Exec-managerial| Husband| White| Male| 0| 0| 45| United-States| >50K| | 31| Private| 45781| Masters| 14| Never-married| Prof-specialty| Not-in-family| White| Female| 14084| 0| 50| United-States| >50K| | 42| Private|159449| Bachelors| 13| Married-civ-spouse| Exec-managerial| Husband| White| Male| 5178| 0| 40| United-States| >50K| | 37| Private|280464| Some-college| 10| Married-civ-spouse| Exec-managerial| Husband| Black| Male| 0| 0| 80| United-States| >50K| | 30| State-gov|141297| Bachelors| 13| Married-civ-spouse| Prof-specialty| Husband| Asian-Pac-Islander| Male| 0| 0| 40| India| >50K| | 23| Private|122272| Bachelors| 13| Never-married| Adm-clerical| Own-child| White| Female| 0| 0| 30| United-States| <=50K| | 32| Private|205019| Assoc-acdm| 12| Never-married| Sales| Not-in-family| Black| Male| 0| 0| 50| United-States| <=50K| | 40| Private|121772| Assoc-voc| 11| Married-civ-spouse| Craft-repair| Husband| Asian-Pac-Islander| Male| 0| 0| 40| ?| >50K| | 34| Private|245487| 7th-8th| 4| Married-civ-spouse| Transport-moving| Husband| Amer-Indian-Eskimo| Male| 0| 0| 45| Mexico| <=50K| | 25| Self-emp-not-inc|176756| HS-grad| 9| Never-married| Farming-fishing| Own-child| White| Male| 0| 0| 35| United-States| <=50K| | 32| Private|186824| HS-grad| 9| Never-married| Machine-op-inspct| Unmarried| White| Male| 0| 0| 40| United-States| <=50K| | 38| Private| 28887| 11th| 7| Married-civ-spouse| Sales| Husband| White| Male| 0| 0| 50| United-States| <=50K| +---+-----------------+------+-------------+-------------+--------------------+------------------+--------------+-------------------+-------+------------+------------+--------------+--------------+------+ only showing top 20 rows
df = sdf.toPandas()
df.head()
_c0 | _c1 | _c2 | _c3 | _c4 | _c5 | _c6 | _c7 | _c8 | _c9 | _c10 | _c11 | _c12 | _c13 | _c14 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | age | workclass | fnlwgt | education | education_num | marital_status | occupation | relationship | race | sex | capital_gain | capital_loss | hours_per_week | native_country | target |
1 | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | Not-in-family | White | Male | 2174 | 0 | 40 | United-States | <=50K |
2 | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Husband | White | Male | 0 | 0 | 13 | United-States | <=50K |
3 | 38 | Private | 215646 | HS-grad | 9 | Divorced | Handlers-cleaners | Not-in-family | White | Male | 0 | 0 | 40 | United-States | <=50K |
4 | 53 | Private | 234721 | 11th | 7 | Married-civ-spouse | Handlers-cleaners | Husband | Black | Male | 0 | 0 | 40 | United-States | <=50K |
sdf.rdd
MapPartitionsRDD[59] at javaToPython at null:-2
sdf.schema
StructType(List(StructField(_c0,StringType,true),StructField(_c1,StringType,true),StructField(_c2,StringType,true),StructField(_c3,StringType,true),StructField(_c4,StringType,true),StructField(_c5,StringType,true),StructField(_c6,StringType,true),StructField(_c7,StringType,true),StructField(_c8,StringType,true),StructField(_c9,StringType,true),StructField(_c10,StringType,true),StructField(_c11,StringType,true),StructField(_c12,StringType,true),StructField(_c13,StringType,true),StructField(_c14,StringType,true)))
sdf.printSchema()
root |-- _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)
On utilise pandas sur une partie du stream.
import pandas
df = pandas.read_csv("data_adult.txt", sep="\t", encoding="utf-8")
df.head(n=2)
age | workclass | fnlwgt | education | education_num | marital_status | occupation | relationship | race | sex | capital_gain | capital_loss | hours_per_week | native_country | target | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | Not-in-family | White | Male | 2174 | 0 | 40 | United-States | <=50K |
1 | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Husband | White | Male | 0 | 0 | 13 | United-States | <=50K |
sdf = spark.createDataFrame(df)
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)
fullsdf = spark.createDataFrame(sdf.rdd, sdf.schema)
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) |-- 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)
fullsdf.write.parquet("data_adult.schema.parquet")
newsdf = spark.read.parquet("data_adult.schema.parquet/")
newsdf.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)
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.
fifty = fullsdf [fullsdf.age > 50]
fifty.show()
+---+-----------------+------+-------------+-------------+-------------------+------------------+---------------+-------------------+-------+------------+------------+--------------+--------------+------+ |age| workclass|fnlwgt| education|education_num| marital_status| occupation| relationship| race| sex|capital_gain|capital_loss|hours_per_week|native_country|target| +---+-----------------+------+-------------+-------------+-------------------+------------------+---------------+-------------------+-------+------------+------------+--------------+--------------+------+ | 53| Private|234721| 11th| 7| Married-civ-spouse| Handlers-cleaners| Husband| Black| Male| 0| 0| 40| United-States| <=50K| | 52| Self-emp-not-inc|209642| HS-grad| 9| Married-civ-spouse| Exec-managerial| Husband| White| Male| 0| 0| 45| United-States| >50K| | 54| Private|302146| HS-grad| 9| Separated| Other-service| Unmarried| Black| Female| 0| 0| 20| United-States| <=50K| | 59| Private|109015| HS-grad| 9| Divorced| Tech-support| Unmarried| White| Female| 0| 0| 40| United-States| <=50K| | 56| Local-gov|216851| Bachelors| 13| Married-civ-spouse| Tech-support| Husband| White| Male| 0| 0| 40| United-States| >50K| | 54| ?|180211| Some-college| 10| Married-civ-spouse| ?| Husband| Asian-Pac-Islander| Male| 0| 0| 60| South| >50K| | 53| Self-emp-not-inc| 88506| Bachelors| 13| Married-civ-spouse| Prof-specialty| Husband| White| Male| 0| 0| 40| United-States| <=50K| | 57| Federal-gov|337895| Bachelors| 13| Married-civ-spouse| Prof-specialty| Husband| Black| Male| 0| 0| 40| United-States| >50K| | 53| Private|144361| HS-grad| 9| Married-civ-spouse| Machine-op-inspct| Husband| White| Male| 0| 0| 38| United-States| <=50K| | 53| Private|169846| HS-grad| 9| Married-civ-spouse| Adm-clerical| Wife| White| Female| 0| 0| 40| United-States| >50K| | 79| Private|124744| Some-college| 10| Married-civ-spouse| Prof-specialty| Other-relative| White| Male| 0| 0| 20| United-States| <=50K| | 67| ?|212759| 10th| 6| Married-civ-spouse| ?| Husband| White| Male| 0| 0| 2| United-States| <=50K| | 52| Private|276515| Bachelors| 13| Married-civ-spouse| Other-service| Husband| White| Male| 0| 0| 40| Cuba| <=50K| | 59| Private|159937| HS-grad| 9| Married-civ-spouse| Sales| Husband| White| Male| 0| 0| 48| United-States| <=50K| | 53| Private|346253| HS-grad| 9| Divorced| Sales| Own-child| White| Female| 0| 0| 35| United-States| <=50K| | 57| Private|249977| Assoc-voc| 11| Married-civ-spouse| Prof-specialty| Husband| White| Male| 0| 0| 40| United-States| <=50K| | 76| Private|124191| Masters| 14| Married-civ-spouse| Exec-managerial| Husband| White| Male| 0| 0| 40| United-States| >50K| | 56| Self-emp-not-inc|335605| HS-grad| 9| Married-civ-spouse| Other-service| Husband| White| Male| 0| 1887| 50| Canada| >50K| | 53| Private| 95647| 9th| 5| Married-civ-spouse| Handlers-cleaners| Husband| White| Male| 0| 0| 50| United-States| <=50K| | 56| Self-emp-inc|303090| Some-college| 10| Married-civ-spouse| Sales| Husband| White| Male| 0| 0| 50| United-States| <=50K| +---+-----------------+------+-------------+-------------+-------------------+------------------+---------------+-------------------+-------+------------+------------+--------------+--------------+------+ only showing top 20 rows