PySpark Poradnik dla początkujących: Ucz się na PRZYKŁADACH

⚡ Inteligentne podsumowanie

PySpark jest Python API dla Apache Spark, umożliwiając analitykom uruchamianie rozproszonego przetwarzania danych, zapytań SQL i uczenia maszynowego w klastrach. Ten przewodnik instaluje go i wyjaśnia SparkKontekst i SQLContext, a następnie buduje klasyfikator regresji logistycznej.

  • 🔘 Podstawy silnika: Spark planuje, rozdziela i monitoruje pracę w klastrze maszyn roboczych,ping obliczenia w pamięci.
  • ️ Python most: PySpark udostępnia RDD, DataFrames i MLlib Python, dzięki czemu użytkownicy platformy Pandas mogą skalować ją poza ograniczenia pojedynczej maszyny.
  • ✅ Dwie ścieżki instalacji: Obraz Dockera na instancji AWS lub dedykowane środowisko Conda Windows i Mac.
  • 🧪 Punkty wejścia: SparkKontekst otwiera połączenie klastra; SQLContext (SparkSesja w nowoczesnych wydaniach) tworzy ramki danych z plików.
  • 🛠️. API potoku: StringIndexer, OneHotEncoder i VectorAssembler składają elementy w taki sam sposób, w jaki robią to potoki scikit-learn.
  • 📊 Zysk z dostrajania: Walidacja krzyżowa parametru regularyzacji podnosi dokładność klasyfikatora zbioru danych dla dorosłych z 82.376% do 85.316%.

PySpark Samouczek dla początkujących

Przed nauką PySpark, zrozumiemy:

Co to jest Apache Spark?

Spark to rozwiązanie do obsługi dużych zbiorów danych, które okazało się łatwiejsze i szybsze niż Hadoop MapReduce. Spark to oprogramowanie typu open source opracowane przez laboratorium UC Berkeley RAD w 2009 r. Od czasu jego publicznego udostępnienia w 2010 r. Spark zyskała na popularności i jest wykorzystywana w przemyśle na niespotykaną dotąd skalę.

W erze Big Data praktycy potrzebują bardziej niż kiedykolwiek szybkich i niezawodnych narzędzi do przetwarzania strumieni danych. Wcześniejsze narzędzia, takie jak MapReduce, były popularne, ale działały wolno. Aby rozwiązać ten problem, Spark oferuje rozwiązanie, które jest zarówno szybkie, jak i uniwersalne. Główna różnica między Spark i MapReduce to jest to Spark wykonuje obliczenia w pamięci podczas późniejszego na dysku twardym. Umożliwia szybki dostęp i przetwarzanie danych, skracając czas z godzin do minut.

Co to jest PySpark?

PySpark to narzędzie stworzone przez firmę Apache Spark Społeczność do korzystania Python w Spark. Umożliwia pracę z RDD (Resilient Distributed Dataset) w Python. Oferuje również PySpark Powłoka do połączenia Python API z Spark rdzeń do zainicjowania Spark Kontekst. Spark jest nazwą silnika do realizacji obliczeń klastrowych, podczas gdy PySpark is Pythonbibliotekę do użycia Spark.

Biblioteka wysyła jako pyspark pakiet na PyPI, więc pojedynczy pip install pyspark pobiera silnik Scala/JVM i most Py4J, który umożliwia Python obiekty do niego odwołują się. Wszystko poniżej bazuje na tym jednym pakiecie.

Jak działa Spark działa?

Spark Opiera się na silniku obliczeniowym, co oznacza, że ​​zajmuje się planowaniem, dystrybucją i monitorowaniem aplikacji. Każde zadanie jest wykonywane na różnych maszynach roboczych, zwanych klastrem obliczeniowym. Klaster obliczeniowy odnosi się do podziału zadań. Jedna maszyna wykonuje jedno zadanie, podczas gdy pozostałe przyczyniają się do końcowego wyniku poprzez inne zadanie. Ostatecznie wszystkie zadania są agregowane w celu wygenerowania wyniku. Spark administrator daje przegląd 360 różnych Spark Oferty pracy.

Poniższy diagram pokazuje, w jaki sposób program sterujący przekazuje zadania maszynom roboczym, które tworzą taki klaster.

Spark klaster obliczeniowy pokazujący zadania planowania sterownika na maszynach roboczych

W jaki sposób Spark Maksymalna wysokość

Spark jest przeznaczony do pracy z

  • Python
  • Java
  • Scala
  • SQL

Istotną cechą Spark to ogromna ilość wbudowanych bibliotek, w tym MLlib do uczenia maszynowego. Spark jest również zaprojektowany do pracy z klastrami Hadoop i może odczytywać szeroki zakres plików, w tym dane Hive, CSV, JSON, Cassandra dane i inne.

Dlaczego warto korzystać Spark?

Jako przyszły praktyk danych powinieneś znać Pythonsłynne biblioteki: Pandy i nauka-scikitTe dwie biblioteki doskonale nadają się do eksploracji zbiorów danych o średniej wielkości. Standardowe projekty uczenia maszynowego opierają się na następującej metodologii:

  • Załaduj dane na dysk
  • Zaimportuj dane do pamięci urządzenia
  • Przetwarzaj/analizuj dane
  • Zbuduj model uczenia maszynowego
  • Zapisz prognozę z powrotem na dysk

Problem pojawia się, gdy analityk danych chce przetworzyć dane, które są zbyt duże dla jednego komputera. We wczesnych etapach rozwoju analityki danych, praktycy próbkowali dane, ponieważ trenowanie na ogromnych zbiorach danych nie zawsze było konieczne. Analityk danych znajdował dobrą próbkę statystyczną, przeprowadzał dodatkową kontrolę odporności i tworzył doskonały model.

Jednak są z tym pewne problemy:

  • Czy zbiór danych odzwierciedla świat rzeczywisty?
  • Czy dane obejmują konkretny przykład?
  • Czy model nadaje się do pobrania?

Weźmy na przykład rekomendacje użytkowników. Osoby polecające polegają na porównywaniu użytkowników z innymi użytkownikami przy ocenie ich preferencji. Jeśli specjalista ds. danych weźmie pod uwagę tylko podzbiór danych, nie będzie kohorty użytkowników, którzy będą do siebie bardzo podobni. Programy rekomendujące muszą działać na pełnym zestawie danych lub wcale.

Jakie jest rozwiązanie?

Rozwiązanie jest oczywiste od dawna, rozbicie problemu na wiele komputerów. Równoległe przetwarzanie również wiąże się z wieloma problemami. Programiści często mają problemy z pisaniem równoległego kodu i kończą na konieczności rozwiązania wielu złożonych problemów związanych z samym przetwarzaniem wieloprocesorowym.

PySpark udostępnia naukowcom zajmującym się danymi interfejs API, który może być używany do rozwiązywania problemów z równoległym przetwarzaniem danych. PySpark radzi sobie ze złożonością przetwarzania wieloprocesowego, np. dystrybucją danych, dystrybucją kodu i zbieraniem danych wyjściowych od pracowników na klastrze maszyn.

Spark może działać samodzielnie, ale najczęściej działa na platformie klastra obliczeniowego, takiej jak Hadoop. Jednak w testach i rozwoju naukowiec zajmujący się danymi może wydajnie działać Spark na swoich komputerach deweloperskich lub laptopach bez klastra

  • Jedną z głównych zalet Spark jest zbudowanie architektury obejmującej zarządzanie strumieniowaniem danych, płynne zapytania o dane, przewidywanie za pomocą uczenia maszynowego i dostęp w czasie rzeczywistym do różnych analiz.
  • Spark ściśle współpracuje z SQL język, czyli dane strukturalne. Umożliwia on wyszukiwanie danych w czasie rzeczywistym.
  • Głównym zadaniem Data Scientist jest analiza i budowanie modeli predykcyjnych. Krótko mówiąc, Data Scientist musi umieć wyszukiwać dane za pomocą SQL, tworzyć raporty statystyczne i wykorzystywać uczenie maszynowe do tworzenia prognoz. Data Scientist poświęca znaczną część swojego czasu na oczyszczanie, transformację i analizę danych. Po przygotowaniu zbioru danych lub przepływu pracy, Data Scientist wykorzystuje różne techniki, aby odkrywać spostrzeżenia i ukryte wzorce. Manipulacja danymi powinna być niezawodna i łatwa w użyciu. Spark to właściwe narzędzie ze względu na szybkość i bogate interfejsy API.

W tym PSpark tutorial, dowiesz się jak zbudować klasyfikator za pomocą PySpark przykłady.

Jak zainstalować PySpark z AWS

Jupyter zespół zbudował obraz Dockera do uruchomienia Spark wydajnie. Poniżej znajdują się kroki, które możesz wykonać, aby zainstalować PySpark instancja w AWS.

Zapoznaj się z naszym samouczkiem na temat AWS oraz TensorFlow

Krok 1: Utwórz instancję

Przede wszystkim musisz utworzyć instancję. Przejdź do swojego konta AWS i uruchom instancję. Możesz zwiększyć pamięć do 15 g i użyć tej samej grupy zabezpieczeń, co w samouczku TensorFlow.

Krok 2: Otwórz połączenie

Otwórz połączenie i zainstaluj kontener docker. Aby uzyskać więcej szczegółów, zapoznaj się z samouczkiem z TensorFlow z Doker. Pamiętaj, że musisz znajdować się we właściwym katalogu roboczym.

Po prostu uruchom te kody, aby zainstalować Docker:

sudo yum update -y
sudo yum install -y docker
sudo service docker start
sudo user-mod -a -G docker ec2-user
exit

Krok 3: Otwórz ponownie połączenie i zainstaluj Spark

Po ponownym otwarciu połączenia możesz zainstalować obraz zawierający PySpark.

## Spark
docker run -v ~/work:/home/jovyan/work -d -p 8888:8888 jupyter/pyspark-notebook

## Allow preserving Jupyter notebook
sudo chown 1000 ~/work

## Install tree to see our working directory next
sudo yum install -y tree

Krok 4: Otwórz Jupyter

Sprawdź pojemnik i jego nazwę

docker ps

Uruchom okno dokowane z dziennikami dokującymi, po których następuje nazwa okna dokowanego. Na przykład doker loguje zealous_goldwasser

Przejdź do przeglądarki i uruchom Jupyter. Adres to http://localhost:8888/. Wklej hasło podane przez terminal.

Uwaga: jeśli chcesz przesłać/pobrać plik na swoją maszynę AWS, możesz skorzystać z oprogramowania Cyberduck.

Jak zainstalować PySpark on Windows/Mac z Condą

Poniżej znajduje się szczegółowy proces instalacji PySpark on Windows/Mac używający Anacondy:

Aby zainstalować Spark na komputerze lokalnym zalecaną praktyką jest utworzenie nowego środowiska Conda. To nowe środowisko zostanie zainstalowane Python 3.6, Spark i wszystkie zależności.

Użytkownik Mac

cd anaconda3
touch hello-spark.yml
vi hello-spark.yml

Windows System Rezerwacji Sal Smart Spaces® rozwiązuje problem zarządzania rezerwacjami sal, biurek i przestrzeni wspólnych. Zmiany w ostatniej chwili są od razu widoczne, co ułatwia pracę recepcji i użytkownikom. Kalendarz w czasie rzeczywistym pokazuje wolne zasoby, ich parametry, a nawet zaplanowane sprzątanie.

cd C:\Users\Admin\Anaconda3
echo.>hello-spark.yml
notepad hello-spark.yml

Możesz edytować plik .yml. Zachowaj ostrożność przy wcięciu. Przed – wymagane są dwie spacje –

name: hello-spark 
    dependencies:
    
    - python=3.6
    - jupyter
    - ipython
    - numpy
    - numpy-base
    - pandas
    - py4j
    - pyspark
    - pytz

Zapisz go i stwórz środowisko. To zajmuje trochę czasu

conda env create -f hello-spark.yml

Aby uzyskać więcej szczegółów na temat lokalizacji, zapoznaj się z samouczkiem Zainstaluj TensorFlow

Możesz sprawdzić całe środowisko zainstalowane na twoim komputerze

conda env list
Activate hello-spark

Użytkownik Mac

source activate hello-spark

Windows System Rezerwacji Sal Smart Spaces® rozwiązuje problem zarządzania rezerwacjami sal, biurek i przestrzeni wspólnych. Zmiany w ostatniej chwili są od razu widoczne, co ułatwia pracę recepcji i użytkownikom. Kalendarz w czasie rzeczywistym pokazuje wolne zasoby, ich parametry, a nawet zaplanowane sprzątanie.

activate hello-spark

Uwaga: Utworzyłeś już określone środowisko TensorFlow, aby uruchomić samouczki dotyczące TensorFlow. Wygodniej jest utworzyć nowe środowisko inne niż hello-tf. Nie ma sensu przeciążać hello-tf Spark lub inne biblioteki uczenia maszynowego.

Wyobraź sobie, że większość Twojego projektu obejmuje TensorFlow, ale musisz go użyć Spark dla jednego konkretnego projektu. Możesz ustawić środowisko TensorFlow dla całego swojego projektu i utworzyć osobne środowisko dla Spark. Możesz dodać dowolną liczbę bibliotek Spark środowisko, jak chcesz, bez ingerencji w środowisko TensorFlow. Gdy już skończysz z Sparkprojektu, możesz go usunąć bez wpływu na środowisko TensorFlow.

Jupyter

Otwarte Jupyter Notatnik i spróbuj, jeśli PySpark działa. W nowym notatniku wklej następujący PySpark przykładowy kod:

import pyspark
from pyspark import SparkContext
sc =SparkContext()

Po pomyślnym uruchomieniu nie zostanie wydrukowany żaden wynik, jak pokazuje zrzut ekranu notatnika poniżej.

Jupyter Komórka notatnika importująca pyspark i tworząca SparkObiekt kontekstowy

Jeśli zostanie wyświetlony błąd, jest to prawdopodobne Java nie jest zainstalowany na twoim komputerze. Na komputerze Mac otwórz terminal i wpisz java -version, jeśli jest wersja java, upewnij się, że jest to 1.8. W Windows, przejdź do aplikacji i sprawdź, czy istnieje Java falcówka. Jeśli istnieje Java folderze, sprawdź to Java Wersja 1.8 jest zainstalowana. W chwili pisania tego tekstu PySpark nie jest kompatybilny z Java 9 i wyżej.

Uwaga dotycząca wersji: że Java 8 ograniczenie dotyczy Spark Linia 2.x używana w tym przewodniku. Aktualna Spark Wersje 3.x i 4.x działają na Java 8, 11 i 17, więc nowszy JDK będzie odpowiedni, jeśli zainstalujesz pasującą wersję PySpark wersja.

Jeśli musisz zainstalować Java, żebyś pomyślał link i pobierz jdk-8u181-windows-x64.exe

W przypadku użytkowników komputerów Mac zaleca się użycie `brew.`

brew tap caskroom/versions
brew cask install java8

Zapoznaj się z tym samouczkiem krok po kroku jak zainstalować Java

Uwaga: Użyj polecenia remove, aby całkowicie usunąć środowisko.

 conda env remove -n hello-spark -y

Spark Kontekst

SparkKontekst to wewnętrzny silnik, który umożliwia połączenia z klastrami. Jeśli chcesz uruchomić operację, potrzebujesz SparkKontekst.

Stwórz SparkKontekst

Przede wszystkim należy zainicjować SparkKontekst.

import pyspark
from pyspark import SparkContext
sc =SparkContext()

Teraz, gdy SparkKontekst jest gotowy, możesz utworzyć zbiór danych o nazwie RDD, Resilient Distributed Dataset. Obliczenia w RDD są automatycznie paralelizowane w całym klastrze.

nums= sc.parallelize([1,2,3,4])

Dostęp do pierwszego wiersza można uzyskać za pomocą polecenia take

nums.take(1)
[1]

Możesz zastosować transformację danych za pomocą funkcji lambda. W PySpark przykład poniżej, zwracasz kwadrat liczb. To transformacja mapy

squared = nums.map(lambda x: x*x).collect()
for num in squared:
    print('%i ' % (num))
1 
4 
9 
16

Kontekst SQL

Wygodniejszym sposobem jest użycie DataFrame. SparkKontekst jest już ustawiony, możesz go użyć do utworzenia ramki danych. Musisz również zadeklarować kontekst SQLContext.

SQLContext umożliwia połączenie silnika z różnymi źródłami danych. Służy do inicjowania funkcjonalności Spark kw.

Uwaga dotycząca wersji: SQLContext został zastąpiony przez SparkSesja w Spark 2.0 i formalnie przestarzałe w Spark 3.0. Poniższe przykłady zachowują SQLContext, ponieważ na nim działa oryginalny przewodnik; w nowoczesnym klastrze odpowiedni punkt wejścia to SparkSession.builder.getOrCreate(), który udostępnia te same metody read i createDataFrame.

from pyspark.sql import Row
from pyspark.sql import SQLContext

sqlContext = SQLContext(sc)

Teraz w tym Spark Tutorial Python, utwórzmy listę krotek. Każda krotka będzie zawierać imię i nazwisko osoby oraz jej wiek. Wymagane są cztery kroki:

Krok 1) Utwórz listę krotek z informacjami

[('John',19),('Smith',29),('Adam',35),('Henry',50)]

Krok 2) Zbuduj RDD

rdd = sc.parallelize(list_p)

Krok 3) Konwertuj krotki

rdd.map(lambda x: Row(name=x[0], age=int(x[1])))

Krok 4) Utwórz kontekst DataFrame

sqlContext.createDataFrame(ppl)
list_p = [('John',19),('Smith',29),('Adam',35),('Henry',50)]
rdd = sc.parallelize(list_p)
ppl = rdd.map(lambda x: Row(name=x[0], age=int(x[1])))
DF_ppl = sqlContext.createDataFrame(ppl)

Jeśli chcesz uzyskać dostęp do typu każdej funkcji, możesz użyć printSchema()

DF_ppl.printSchema()
root
 |-- age: long (nullable = true)
 |-- name: string (nullable = true)

Przykład uczenia maszynowego z PySpark

Teraz, gdy masz już krótkie pojęcie Spark i SQLContext, możesz już zbudować swój pierwszy program do uczenia maszynowego.

Poniżej przedstawiono kroki tworzenia programu uczenia maszynowego przy użyciu języka PySpark:

  • Krok 1) Podstawowa obsługa PySpark
  • Krok 2) Wstępne przetwarzanie danych
  • Krok 3) Zbuduj potok przetwarzania danych
  • Krok 4) Zbuduj klasyfikator: logistyczny
  • Krok 5) Wytrenuj i oceń model
  • Krok 6) Dostosuj hiperparametr

W tym PSpark W samouczku uczenia maszynowego wykorzystamy zbiór danych dla dorosłych. Celem tego samouczka jest nauczenie się korzystania z języka Py.SparkWięcej informacji na temat zestawu danych znajdziesz w tym samouczku.

Należy pamiętać, że zbiór danych nie jest znaczący i może się wydawać, że obliczenia zajmują dużo czasu. Spark jest przeznaczony do przetwarzania znacznej ilości danych. SparkWydajność biblioteki zwiększa się w porównaniu do innych bibliotek uczenia maszynowego, gdy zbiór przetwarzanych danych staje się większy.

Krok 1) Podstawowa obsługa PySpark

Przede wszystkim musisz zainicjować SQLContext, który nie jest jeszcze zainicjowany.

#from pyspark.sql import SQLContext
url = "https://raw.githubusercontent.com/guru99-edu/R-Programming/master/adult_data.csv"
from pyspark import SparkFiles
sc.addFile(url)
sqlContext = SQLContext(sc)

następnie możesz przeczytać plik cvs za pomocą sqlContext.read.csv. Aby to stwierdzić, używasz inferSchema ustawionego na True Spark aby automatycznie odgadnąć typ danych. Domyślnie jest to wartość False.

df = sqlContext.read.csv(SparkFiles.get("adult_data.csv"), header=True, inferSchema= True)

Przyjrzyjmy się typowi danych

df.printSchema()
root
 |-- age: integer (nullable = true)
 |-- workclass: string (nullable = true)
 |-- fnlwgt: integer (nullable = true)
 |-- education: string (nullable = true)
 |-- education_num: integer (nullable = true)
 |-- marital: string (nullable = true)
 |-- occupation: string (nullable = true)
 |-- relationship: string (nullable = true)
 |-- race: string (nullable = true)
 |-- sex: string (nullable = true)
 |-- capital_gain: integer (nullable = true)
 |-- capital_loss: integer (nullable = true)
 |-- hours_week: integer (nullable = true)
 |-- native_country: string (nullable = true)
 |-- label: string (nullable = true)

Możesz zobaczyć dane za pomocą show.

df.show(5, truncate = False)
+---+----------------+------+---------+-------------+------------------+-----------------+-------------+-----+------+------------+------------+----------+--------------+-----+
|age|workclass       |fnlwgt|education|education_num|marital           |occupation       |relationship |race |sex   |capital_gain|capital_loss|hours_week|native_country|label|
+---+----------------+------+---------+-------------+------------------+-----------------+-------------+-----+------+------------+------------+----------+--------------+-----+
|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|
+---+----------------+------+---------+-------------+------------------+-----------------+-------------+-----+------+------------+------------+----------+--------------+-----+
only showing top 5 rows

Jeśli nie ustawiłeś parametru inferSchema na True, oto co dzieje się z typem: Wszystkie są w ciągu znaków.

df_string = sqlContext.read.csv(SparkFiles.get("adult.csv"), header=True, inferSchema=  False)
df_string.printSchema()
root
 |-- age: string (nullable = true)
 |-- workclass: string (nullable = true)
 |-- fnlwgt: string (nullable = true)
 |-- education: string (nullable = true)
 |-- education_num: string (nullable = true)
 |-- marital: string (nullable = true)
 |-- occupation: string (nullable = true)
 |-- relationship: string (nullable = true)
 |-- race: string (nullable = true)
 |-- sex: string (nullable = true)
 |-- capital_gain: string (nullable = true)
 |-- capital_loss: string (nullable = true)
 |-- hours_week: string (nullable = true)
 |-- native_country: string (nullable = true)
 |-- label: string (nullable = true)

Aby przekonwertować zmienną ciągłą na odpowiedni format, możesz użyć przekształcenia kolumn. Możesz użyć withColumn, aby to powiedzieć Spark w której kolumnie przeprowadzić transformację.

# Import all from `sql.types`
from pyspark.sql.types import *

# Write a custom function to convert the data type of DataFrame columns
def convertColumn(df, names, newType):
    for name in names: 
        df = df.withColumn(name, df[name].cast(newType))
    return df 
# List of continuous features
CONTI_FEATURES  = ['age', 'fnlwgt','capital_gain', 'education_num', 'capital_loss', 'hours_week']
# Convert the type
df_string = convertColumn(df_string, CONTI_FEATURES, FloatType())
# Check the dataset
df_string.printSchema()
root
 |-- age: float (nullable = true)
 |-- workclass: string (nullable = true)
 |-- fnlwgt: float (nullable = true)
 |-- education: string (nullable = true)
 |-- education_num: float (nullable = true)
 |-- marital: string (nullable = true)
 |-- occupation: string (nullable = true)
 |-- relationship: string (nullable = true)
 |-- race: string (nullable = true)
 |-- sex: string (nullable = true)
 |-- capital_gain: float (nullable = true)
 |-- capital_loss: float (nullable = true)
 |-- hours_week: float (nullable = true)
 |-- native_country: string (nullable = true)
 |-- label: string (nullable = true)

from pyspark.ml.feature import StringIndexer
#stringIndexer = StringIndexer(inputCol="label", outputCol="newlabel")
#model = stringIndexer.fit(df)
#df = model.transform(df)
df.printSchema()

Wybierz kolumny

Możesz wybrać i wyświetlić wiersze z zaznaczeniem oraz nazwy funkcji. Poniżej wybrano wiek i fnlwgt.

df.select('age','fnlwgt').show(5)
+---+------+
|age|fnlwgt|
+---+------+
| 39| 77516|
| 50| 83311|
| 38|215646|
| 53|234721|
| 28|338409|
+---+------+
only showing top 5 rows

Licz według grup

Jeśli chcesz policzyć liczbę wystąpień według grupy, możesz to zrobić łańcuchowo:

  • Grupuj według()
  • liczyć()

razem. W PySpark w poniższym przykładzie liczysz liczbę wierszy według poziomu wykształcenia.

df.groupBy("education").count().sort("count",ascending=True).show()
+------------+-----+
|   education|count|
+------------+-----+
|   Preschool|   51|
|     1st-4th|  168|
|     5th-6th|  333|
|   Doctorate|  413|
|        12th|  433|
|         9th|  514|
| Prof-school|  576|
|     7th-8th|  646|
|        10th|  933|
|  Assoc-acdm| 1067|
|        11th| 1175|
|   Assoc-voc| 1382|
|     Masters| 1723|
|   Bachelors| 5355|
|Some-college| 7291|
|     HS-grad|10501|
+------------+-----+

Opisz dane

Aby uzyskać podsumowanie statystyk danych, możesz użyć funkcji opisu (). Obliczy:

  • liczyć
  • oznaczać
  • odchylenie standardowe
  • min
  • max
df.describe().show()
+-------+------------------+-----------+------------------+------------+-----------------+--------+----------------+------------+------------------+------+------------------+----------------+------------------+--------------+-----+
|summary|               age|  workclass|            fnlwgt|   education|    education_num| marital|      occupation|relationship|              race|   sex|      capital_gain|    capital_loss|        hours_week|native_country|label|
+-------+------------------+-----------+------------------+------------+-----------------+--------+----------------+------------+------------------+------+------------------+----------------+------------------+--------------+-----+
|  count|             32561|      32561|             32561|       32561|            32561|   32561|           32561|       32561|             32561| 32561|             32561|           32561|             32561|         32561|32561|
|   mean| 38.58164675532078|       null|189778.36651208502|        null| 10.0806793403151|    null|            null|        null|              null|  null|1077.6488437087312| 87.303829734959|40.437455852092995|          null| null|
| stddev|13.640432553581356|       null|105549.97769702227|        null|2.572720332067397|    null|            null|        null|              null|  null| 7385.292084840354|402.960218649002|12.347428681731838|          null| null|
|    min|                17|          ?|             12285|        10th|                1|Divorced|               ?|     Husband|Amer-Indian-Eskimo|Female|                 0|               0|                 1|             ?|<=50K|
|    max|                90|Without-pay|           1484705|Some-college|               16| Widowed|Transport-moving|        Wife|             White|  Male|             99999|            4356|                99|    Yugoslavia| >50K|
+-------+------------------+-----------+------------------+------------+-----------------+--------+----------------+------------+------------------+------+------------------+----------------+------------------+--------------+-----+

Jeśli chcesz uzyskać statystykę podsumowującą tylko jedną kolumnę, dodaj nazwę kolumny wewnątrz funkcji opisu()

df.describe('capital_gain').show()
+-------+------------------+
|summary|      capital_gain|
+-------+------------------+
|  count|             32561|
|   mean|1077.6488437087312|
| stddev| 7385.292084840354|
|    min|                 0|
|    max|             99999|
+-------+------------------+

Obliczenia krzyżowe

W niektórych przypadkach może być interesujące zobaczenie statystyk opisowych między dwiema parami kolumn. Na przykład możesz policzyć liczbę osób z dochodem poniżej lub powyżej 50 tys. według poziomu wykształcenia. Ta operacja nazywa się tabelą krzyżową.

df.crosstab('age', 'label').sort("age_label").show()
+---------+-----+----+
|age_label|<=50K|>50K|
+---------+-----+----+
|       17|  395|   0|
|       18|  550|   0|
|       19|  710|   2|
|       20|  753|   0|
|       21|  717|   3|
|       22|  752|  13|
|       23|  865|  12|
|       24|  767|  31|
|       25|  788|  53|
|       26|  722|  63|
|       27|  754|  81|
|       28|  748| 119|
|       29|  679| 134|
|       30|  690| 171|
|       31|  705| 183|
|       32|  639| 189|
|       33|  684| 191|
|       34|  643| 243|
|       35|  659| 217|
|       36|  635| 263|
+---------+-----+----+
only showing top 20 rows

Widać, że nikt w młodym wieku nie osiąga przychodów powyżej 50 tys.

Upuść kolumnę

Istnieją dwa intuicyjne API do upuszczania kolumn:

  • drop(): Upuść kolumnę
  • dropna(): Upuść NA

Poniżej upuszczasz kolumnę edukacja_num

df.drop('education_num').columns

['age',
 'workclass',
 'fnlwgt',
 'education',
 'marital',
 'occupation',
 'relationship',
 'race',
 'sex',
 'capital_gain',
 'capital_loss',
 'hours_week',
 'native_country',
 'label']

Filtruj dane

Możesz użyć filter() aby zastosować statystyki opisowe w podzbiorze danych. Można na przykład policzyć liczbę osób powyżej 40. roku życia

df.filter(df.age > 40).count()

13443

Descript5 statystyk według grup

Na koniec można grupować dane według grup i obliczać operacje statystyczne, takie jak średnia.

df.groupby('marital').agg({'capital_gain': 'mean'}).show()
+--------------------+------------------+
|             marital| avg(capital_gain)|
+--------------------+------------------+
|           Separated| 535.5687804878049|
|       Never-married|376.58831788823363|
|Married-spouse-ab...| 653.9832535885167|
|            Divorced| 728.4148098131893|
|             Widowed| 571.0715005035247|
|   Married-AF-spouse| 432.6521739130435|
|  Married-civ-spouse|1764.8595085470085|
+--------------------+------------------+

Krok 2) Wstępne przetwarzanie danych

Przetwarzanie danych to kluczowy etap uczenia maszynowego. Po usunięciu zbędnych danych uzyskasz kilka ważnych spostrzeżeń.

Na przykład wiesz, że wiek nie jest funkcją liniową z dochodem. Kiedy ludzie są młodzi, ich dochody są zwykle niższe niż w średnim wieku. Po przejściu na emeryturę gospodarstwo domowe korzysta z oszczędności, co oznacza spadek dochodów. Aby uchwycić ten wzór, możesz dodać kwadrat do funkcji wieku

Dodaj kwadrat wieku

Aby dodać nową funkcję należy:

  1. Wybierz kolumnę
  2. Zastosuj transformację i dodaj ją do ramki DataFrame
from pyspark.sql.functions import *

# 1 Select the column
age_square = df.select(col("age")**2)

# 2 Apply the transformation and add it to the DataFrame
df = df.withColumn("age_square", col("age")**2)

df.printSchema()
root
 |-- age: integer (nullable = true)
 |-- workclass: string (nullable = true)
 |-- fnlwgt: integer (nullable = true)
 |-- education: string (nullable = true)
 |-- education_num: integer (nullable = true)
 |-- marital: string (nullable = true)
 |-- occupation: string (nullable = true)
 |-- relationship: string (nullable = true)
 |-- race: string (nullable = true)
 |-- sex: string (nullable = true)
 |-- capital_gain: integer (nullable = true)
 |-- capital_loss: integer (nullable = true)
 |-- hours_week: integer (nullable = true)
 |-- native_country: string (nullable = true)
 |-- label: string (nullable = true)
 |-- age_square: double (nullable = true)

Możesz zobaczyć, że age_square został pomyślnie dodany do ramki danych. Możesz zmienić kolejność zmiennych za pomocą opcji Select. Poniżej wprowadzasz age_square zaraz po wieku.

COLUMNS = ['age', 'age_square', 'workclass', 'fnlwgt', 'education', 'education_num', 'marital',
           'occupation', 'relationship', 'race', 'sex', 'capital_gain', 'capital_loss',
           'hours_week', 'native_country', 'label']
df = df.select(COLUMNS)
df.first()
Row(age=39, age_square=1521.0, workclass='State-gov', fnlwgt=77516, education='Bachelors', education_num=13, marital='Never-married', occupation='Adm-clerical', relationship='Not-in-family', race='White', sex='Male', capital_gain=2174, capital_loss=0, hours_week=40, native_country='United-States', label='<=50K')

Nie obejmuje Holandii i Holandii

Kiedy grupa w obrębie obiektu ma tylko jedną obserwację, nie wnosi ona żadnych informacji do modelu. Wręcz przeciwnie, może to prowadzić do błędu podczas sprawdzania krzyżowego.

Sprawdźmy pochodzenie gospodarstwa domowego

df.filter(df.native_country == 'Holand-Netherlands').count()
df.groupby('native_country').agg({'native_country': 'count'}).sort(asc("count(native_country)")).show()
+--------------------+---------------------+
|      native_country|count(native_country)|
+--------------------+---------------------+
|  Holand-Netherlands|                    1|
|            Scotland|                   12|
|             Hungary|                   13|
|            Honduras|                   13|
|Outlying-US(Guam-...|                   14|
|          Yugoslavia|                   16|
|            Thailand|                   18|
|                Laos|                   18|
|            Cambodia|                   19|
|     Trinadad&Tobago|                   19|
|                Hong|                   20|
|             Ireland|                   24|
|             Ecuador|                   28|
|              Greece|                   29|
|              France|                   29|
|                Peru|                   31|
|           Nicaragua|                   34|
|            Portugal|                   37|
|                Iran|                   43|
|               Haiti|                   44|
+--------------------+---------------------+
only showing top 20 rows

Funkcja native_country ma tylko jedno gospodarstwo domowe pochodzące z Holandii. Wykluczasz to.

df_remove = df.filter(df.native_country !=	'Holand-Netherlands')

Krok 3) Zbuduj potok przetwarzania danych

Podobnie jak scikit-learn, PySpark ma interfejs API dla potoku.

Pipeline jest bardzo wygodny do utrzymania struktury danych. Wpychasz dane do pipeline. Wewnątrz pipeline wykonywane są różne operacje, a dane wyjściowe służą do zasilania algorytmu.

Na przykład jedna uniwersalna transformacja w uczeniu maszynowym polega na konwersji ciągu znaków na jeden gorący koder, tj. jedną kolumnę po grupie. Jeden gorący koder to zwykle macierz pełna zer.

Kroki transformacji danych są bardzo podobne do procesu scikit-learn. Musisz:

  • Indeksuj ciąg znaków jako numeryczny
  • Utwórz jeden gorący koder
  • Przekształć dane

Zadanie to wykonują dwa interfejsy API: StringIndexer, OneHotEncoder

  1. Przede wszystkim wybierasz kolumnę z ciągiem znaków do indeksowania. inputCol to nazwa kolumny w zestawie danych. OutputCol to nowa nazwa nadana przekształconej kolumnie.
StringIndexer(inputCol="workclass", outputCol="workclass_encoded")
  1. Dopasuj dane i przekształć je
model = stringIndexer.fit(df)		
`indexed = model.transform(df)``
  1. Utwórz kolumny wiadomości na podstawie grupy. Na przykład, jeśli w funkcji jest 10 grup, nowa macierz będzie miała 10 kolumn, po jednej dla każdej grupy.
OneHotEncoder(dropLast=False, inputCol="workclassencoded", outputCol="workclassvec")
### Example encoder
from pyspark.ml.feature import StringIndexer, OneHotEncoder, VectorAssembler

stringIndexer = StringIndexer(inputCol="workclass", outputCol="workclass_encoded")
model = stringIndexer.fit(df)
indexed = model.transform(df)
encoder = OneHotEncoder(dropLast=False, inputCol="workclass_encoded", outputCol="workclass_vec")
encoded = encoder.transform(indexed)
encoded.show(2)
+---+----------+----------------+------+---------+-------------+------------------+---------------+-------------+-----+----+------------+------------+----------+--------------+-----+-----------------+-------------+
|age|age_square|       workclass|fnlwgt|education|education_num|           marital|     occupation| relationship| race| sex|capital_gain|capital_loss|hours_week|native_country|label|workclass_encoded|workclass_vec|
+---+----------+----------------+------+---------+-------------+------------------+---------------+-------------+-----+----+------------+------------+----------+--------------+-----+-----------------+-------------+
| 39|    1521.0|       State-gov| 77516|Bachelors|           13|     Never-married|   Adm-clerical|Not-in-family|White|Male|        2174|           0|        40| United-States|<=50K|              4.0|(9,[4],[1.0])|
| 50|    2500.0|Self-emp-not-inc| 83311|Bachelors|           13|Married-civ-spouse|Exec-managerial|      Husband|White|Male|           0|           0|        13| United-States|<=50K|              1.0|(9,[1],[1.0])|
+---+----------+----------------+------+---------+-------------+------------------+---------------+-------------+-----+----+------------+------------+----------+--------------+-----+-----------------+-------------+
only showing top 2 rows

Zbuduj rurociąg

Zbudujesz potok, aby przekonwertować wszystkie precyzyjne funkcje i dodać je do ostatecznego zestawu danych. Potok będzie miał cztery operacje, ale możesz dodać tyle operacji, ile chcesz.

  1. Zakoduj dane kategoryczne
  2. Indeksuj funkcję etykiety
  3. Dodaj zmienną ciągłą
  4. Złóż stopnie.

Każdy krok jest przechowywany na liście nazwanej etapami. Ta lista powie VectorAssemblerowi, jaką operację wykonać w potoku.

1. Zakoduj dane kategoryczne

Ten krok jest dokładnie taki sam jak przykład powyżej, z tą różnicą, że wykonujesz pętlę po wszystkich cechach kategorycznych.

from pyspark.ml import Pipeline
from pyspark.ml.feature import OneHotEncoderEstimator
CATE_FEATURES = ['workclass', 'education', 'marital', 'occupation', 'relationship', 'race', 'sex', 'native_country']
stages = [] # stages in our Pipeline
for categoricalCol in CATE_FEATURES:
    stringIndexer = StringIndexer(inputCol=categoricalCol, outputCol=categoricalCol + "Index")
    encoder = OneHotEncoderEstimator(inputCols=[stringIndexer.getOutputCol()],
                                     outputCols=[categoricalCol + "classVec"])
    stages += [stringIndexer, encoder]

Uwaga dotycząca API: OneHotEncoderEstimator został ponownie połączony z OneHotEncoder w Spark 3.0. Na Spark 3.x lub nowszy, zaimportuj OneHotEncoder i przekaż te same argumenty inputCols i outputCols; reszta pętli pozostaje niezmieniona.

2. Indeksuj element etykiety

Spark, podobnie jak wiele innych bibliotek, nie akceptuje wartości łańcuchowych dla etykiety. Konwertujesz funkcję etykiety za pomocą StringIndexer i dodajesz ją do etapów listy

# Convert label into label indices using the StringIndexer
label_stringIdx =  StringIndexer(inputCol="label", outputCol="newlabel")
stages += [label_stringIdx]

3. Dodaj zmienną ciągłą

Parametr inputCols asemblera VectorAssembler to lista kolumn. Możesz utworzyć nową listę zawierającą wszystkie nowe kolumny. Poniższy kod wypełnia listę zakodowanymi cechami kategorialnymi i ciągłymi.

assemblerInputs = [c + "classVec" for c in CATE_FEATURES] + CONTI_FEATURES

4. Zmontuj stopnie.

Na koniec wykonujesz wszystkie kroki w VectorAssemblerze

assembler = VectorAssembler(inputCols=assemblerInputs, outputCol="features")stages += [assembler]

Teraz, gdy wszystkie kroki są gotowe, możesz wypychać dane do potoku.

# Create a Pipeline.
pipeline = Pipeline(stages=stages)
pipelineModel = pipeline.fit(df_remove)
model = pipelineModel.transform(df_remove)

Jeśli sprawdzisz nowy zbiór danych, zobaczysz, że zawiera on wszystkie funkcje, przekształcone i nie przekształcone. Interesuje Cię tylko nowa etykieta i funkcje. Cechy obejmują wszystkie przekształcone cechy i zmienne ciągłe.

model.take(1)

[Row(age=39, age_square=1521.0, workclass='State-gov', fnlwgt=77516, education='Bachelors', education_num=13, marital='Never-married', occupation='Adm-clerical', relationship='Not-in-family', race='White', sex='Male', capital_gain=2174, capital_loss=0, hours_week=40, native_country='United-States', label='<=50K', workclassIndex=4.0, workclassclassVec=SparseVector(8, {4: 1.0}), educationIndex=2.0, educationclassVec=SparseVector(15, {2: 1.0}), maritalIndex=1.0, maritalclassVec=SparseVector(6, {1: 1.0}), occupationIndex=3.0, occupationclassVec=SparseVector(14, {3: 1.0}), relationshipIndex=1.0, relationshipclassVec=SparseVector(5, {1: 1.0}), raceIndex=0.0, raceclassVec=SparseVector(4, {0: 1.0}), sexIndex=0.0, sexclassVec=SparseVector(1, {0: 1.0}), native_countryIndex=0.0, native_countryclassVec=SparseVector(40, {0: 1.0}), newlabel=0.0, features=SparseVector(99, {4: 1.0, 10: 1.0, 24: 1.0, 32: 1.0, 44: 1.0, 48: 1.0, 52: 1.0, 53: 1.0, 93: 39.0, 94: 77516.0, 95: 2174.0, 96: 13.0, 98: 40.0}))]

Krok 4) Zbuduj klasyfikator: logistyczny

Aby przyspieszyć obliczenia, konwertujesz model na ramkę danych.

Musisz wybrać nową etykietę i funkcje z modelu za pomocą mapy.

from pyspark.ml.linalg import DenseVector
input_data = model.rdd.map(lambda x: (x["newlabel"], DenseVector(x["features"])))

Można już tworzyć dane pociągu w postaci ramki DataFrame. Używasz sqlContext

df_train = sqlContext.createDataFrame(input_data, ["label", "features"])

Sprawdź drugi rząd

df_train.show(2)
+-----+--------------------+
|label|            features|
+-----+--------------------+
|  0.0|[0.0,0.0,0.0,0.0,...|
|  0.0|[0.0,1.0,0.0,0.0,...|
+-----+--------------------+
only showing top 2 rows

Utwórz zestaw pociągowy/testowy

Podzieliłeś zbiór danych 80/20 za pomocą randomSplit.

# Split the data into train and test sets
train_data, test_data = df_train.randomSplit([.8,.2],seed=1234)

Policzmy, ile osób ma dochody poniżej/powyżej 50 tys. zarówno w zbiorze treningowym, jak i testowym

train_data.groupby('label').agg({'label': 'count'}).show()
+-----+------------+
|label|count(label)|
+-----+------------+
|  0.0|       19698|
|  1.0|        6263|
+-----+------------+
test_data.groupby('label').agg({'label': 'count'}).show()
+-----+------------+
|label|count(label)|
+-----+------------+
|  0.0|        5021|
|  1.0|        1578|
+-----+------------+

Zbuduj regresor logistyczny

Na koniec, ale nie mniej ważne, możesz zbudować klasyfikator. PySpark ma API o nazwie LogisticRegression służące do wykonywania regresji logistycznej.

Inicjujesz lr, wskazując kolumnę etykiety i kolumny funkcji. Ustawiasz maksymalnie 10 iteracji i dodajesz parametr regularyzacji o wartości 0.3. Pamiętaj, że w następnej sekcji do dostrojenia modelu użyjesz sprawdzania krzyżowego z siatką parametrów

# Import `LinearRegression`
from pyspark.ml.classification import LogisticRegression

# Initialize `lr`
lr = LogisticRegression(labelCol="label",
                        featuresCol="features",
                        maxIter=10,
                        regParam=0.3)

# Fit the data to the model
linearModel = lr.fit(train_data)

#Możesz zobaczyć współczynniki z regresji

# Print the coefficients and intercept for logistic regression
print("Coefficients: " + str(linearModel.coefficients))
print("Intercept: " + str(linearModel.intercept))
Coefficients: [-0.0678914665262,-0.153425526813,-0.0706009536407,-0.164057586562,-0.120655298528,0.162922330862,0.149176870438,-0.626836362611,-0.193483661541,-0.0782269980838,0.222667203836,0.399571096381,-0.0222024341804,-0.311925857859,-0.0434497788688,-0.306007744328,-0.41318209688,0.547937504247,-0.395837350854,-0.23166535958,0.618743906733,-0.344088614546,-0.385266881369,0.317324463006,-0.350518889186,-0.201335923138,-0.232878560088,-0.13349278865,-0.119760542498,0.17500602491,-0.0480968101118,0.288484253943,-0.116314616745,0.0524163478063,-0.300952624551,-0.22046421474,-0.16557996579,-0.114676231939,-0.311966431453,-0.344226119233,0.105530129507,0.152243047814,-0.292774545497,0.263628334433,-0.199951374076,-0.30329422583,-0.231087515178,0.418918551,-0.0565930184279,-0.177818073048,-0.0733236680663,-0.267972912252,0.168491215697,-0.12181255723,-0.385648075442,-0.202101794517,0.0469791640782,-0.00842850210625,-0.00373211448629,-0.259296141281,-0.309896554133,-0.168434409756,-0.11048086026,0.0280647963877,-0.204187030092,-0.414392623536,-0.252806580669,0.143366465705,-0.516359222663,-0.435627370849,-0.301949286524,0.0878249035894,-0.210951740965,-0.621417928742,-0.099445190784,-0.232671473401,-0.1077745606,-0.360429419703,-0.420362959052,-0.379729467809,-0.395186242741,0.0826401853838,-0.280251589972,0.187313505214,-0.20295228799,-0.431177064626,0.149759018379,-0.107114299614,-0.319314858424,0.0028450133235,-0.651220387649,-0.327918792207,-0.143659581445,0.00691075160413,8.38517628783e-08,2.18856717378e-05,0.0266701216268,0.000231075966823,0.00893832698698]
Intercept: -1.9884177974805692

Krok 5) Wytrenuj i oceń model

Aby wygenerować prognozę dla zestawu testowego,

Możesz użyć linearModel z transform() na test_data

# Make predictions on test data using the transform() method.
predictions = linearModel.transform(test_data)

Możesz wydrukować elementy w prognozach

predictions.printSchema()
root
 |-- label: double (nullable = true)
 |-- features: vector (nullable = true)
 |-- rawPrediction: vector (nullable = true)
 |-- probability: vector (nullable = true)
 |-- prediction: double (nullable = false)

Interesuje Cię etykieta, przewidywanie i prawdopodobieństwo

selected = predictions.select("label", "prediction", "probability")
selected.show(20)
+-----+----------+--------------------+
|label|prediction|         probability|
+-----+----------+--------------------+
|  0.0|       0.0|[0.91560704124179...|
|  0.0|       0.0|[0.92812140213994...|
|  0.0|       0.0|[0.92161406774159...|
|  0.0|       0.0|[0.96222760777142...|
|  0.0|       0.0|[0.66363283056957...|
|  0.0|       0.0|[0.65571324475477...|
|  0.0|       0.0|[0.73053376932829...|
|  0.0|       1.0|[0.31265053873570...|
|  0.0|       0.0|[0.80005907577390...|
|  0.0|       0.0|[0.76482251301640...|
|  0.0|       0.0|[0.84447301189069...|
|  0.0|       0.0|[0.75691912026619...|
|  0.0|       0.0|[0.60902504096722...|
|  0.0|       0.0|[0.80799228385509...|
|  0.0|       0.0|[0.87704364852567...|
|  0.0|       0.0|[0.83817652582377...|
|  0.0|       0.0|[0.79655423248500...|
|  0.0|       0.0|[0.82712311232246...|
|  0.0|       0.0|[0.81372823882016...|
|  0.0|       0.0|[0.59687710752201...|
+-----+----------+--------------------+
only showing top 20 rows

Oceń model

Aby zobaczyć, jak dobrze (lub źle) działa model, należy przyjrzeć się metryce dokładności. Obecnie nie ma interfejsu API umożliwiającego obliczenie miary dokładności Spark. Wartością domyślną jest ROC, krzywa charakterystyki działania odbiornika. Jest to inna metryka, która uwzględnia współczynnik fałszywych wyników dodatnich.

Zanim spojrzysz na ROC, skonstruujmy miarę dokładności. Jesteś bardziej zaznajomiony z tym wskaźnikiem. Miarą dokładności jest suma poprawnych przewidywań na podstawie całkowitej liczby obserwacji.

Tworzysz DataFrame z etykietą i „prediction.

cm = predictions.select("label", "prediction")

Numer klasy możesz sprawdzić na etykiecie oraz w przewidywaniu

cm.groupby('label').agg({'label': 'count'}).show()
+-----+------------+
|label|count(label)|
+-----+------------+
|  0.0|        5021|
|  1.0|        1578|
+-----+------------+
cm.groupby('prediction').agg({'prediction': 'count'}).show()
+----------+-----------------+
|prediction|count(prediction)|
+----------+-----------------+
|       0.0|             5982|
|       1.0|              617|
+----------+-----------------+

Przykładowo w zbiorze testowym znajduje się 1578 gospodarstw domowych o dochodach powyżej 50 tys. i 5021 poniżej. Klasyfikator przewidział natomiast 617 gospodarstw domowych o dochodach powyżej 50 tys.

Dokładność można obliczyć, obliczając liczbę, gdy etykiety są poprawnie sklasyfikowane w całkowitej liczbie wierszy.

cm.filter(cm.label == cm.prediction).count() / cm.count()

0.8237611759357478

Możesz wszystko złożyć w całość i napisać funkcję obliczającą dokładność.

def accuracy_m(model): 
    predictions = model.transform(test_data)
    cm = predictions.select("label", "prediction")
    acc = cm.filter(cm.label == cm.prediction).count() / cm.count()
    print("Model accuracy: %.3f%%" % (acc * 100)) 
accuracy_m(model = linearModel)
Model accuracy: 82.376%

Wskaźniki ROC

Moduł BinaryClassificationEvaluator zawiera miary ROC. Odbiornik Operakrzywa charakterystyczna jest kolejnym powszechnym narzędziem używanym w klasyfikacji binarnej. Jest bardzo podobna do krzywej precyzji/przypomnienia, ale zamiast wykreślać precyzję w funkcji przypominania, krzywa ROC pokazuje prawdziwie dodatni współczynnik (tj. przypominanie) w porównaniu z fałszywie dodatnim współczynnikiem. Odsetek wyników fałszywie pozytywnych to stosunek przypadków negatywnych, które zostały błędnie sklasyfikowane jako pozytywne. Jest ona równa jeden minus rzeczywista stopa ujemna. Prawdziwie ujemna stopa nazywana jest również specyficznością. Stąd krzywa ROC przedstawia czułość (przypominanie) w funkcji 1 – swoistość

### Use ROC 
from pyspark.ml.evaluation import BinaryClassificationEvaluator

# Evaluate model
evaluator = BinaryClassificationEvaluator(rawPredictionCol="rawPrediction")
print(evaluator.evaluate(predictions))
print(evaluator.getMetricName())

0.8940481662695192obszarPodROC

print(evaluator.evaluate(predictions))

0.8940481662695192

Krok 6) Dostosuj hiperparametr

Na koniec, ale nie mniej ważne, możesz dostroić hiperparametry. Podobnie jak w scikit-learn, tworzysz siatkę parametrów i dodajesz parametry, które chcesz dostroić.

Aby skrócić czas obliczeń, parametr regularyzacji dostraja się tylko za pomocą tylko dwóch wartości.

from pyspark.ml.tuning import ParamGridBuilder, CrossValidator

# Create ParamGrid for Cross Validation
paramGrid = (ParamGridBuilder()
             .addGrid(lr.regParam, [0.01, 0.5])
             .build())

Na koniec oceniasz model, stosując metodę walidacji krzyżowej z 5-krotnym testem. Trening trwa około 16 minut.

from time import *
start_time = time()

# Create 5-fold CrossValidator
cv = CrossValidator(estimator=lr,
                    estimatorParamMaps=paramGrid,
                    evaluator=evaluator, numFolds=5)

# Run cross validations
cvModel = cv.fit(train_data)
# likely take a fair amount of time
end_time = time()
elapsed_time = end_time - start_time
print("Time to train model: %.3f seconds" % elapsed_time)

Czas wytrenowania modelu: 978.807 sekundy

Najlepszy hiperparametr regularyzacji wynosi 0.01, z dokładnością 85.316 procent.

accuracy_m(model = cvModel)
Model accuracy: 85.316%

Możesz byćtraczalecany parametr poprzez połączenie cvModel.bestModel z extractParamMap()

bestModel = cvModel.bestModel
bestModel.extractParamMap()
{Param(parent='LogisticRegression_4d8f8ce4d6a02d8c29a0', name='aggregationDepth', doc='suggested depth for treeAggregate (>= 2)'): 2,
 Param(parent='LogisticRegression_4d8f8ce4d6a02d8c29a0', name='elasticNetParam', doc='the ElasticNet mixing parameter, in range [0, 1]. For alpha = 0, the penalty is an L2 penalty. For alpha = 1, it is an L1 penalty'): 0.0,
 Param(parent='LogisticRegression_4d8f8ce4d6a02d8c29a0', name='family', doc='The name of family which is a description of the label distribution to be used in the model. Supported options: auto, binomial, multinomial.'): 'auto',
 Param(parent='LogisticRegression_4d8f8ce4d6a02d8c29a0', name='featuresCol', doc='features column name'): 'features',
 Param(parent='LogisticRegression_4d8f8ce4d6a02d8c29a0', name='fitIntercept', doc='whether to fit an intercept term'): True,
 Param(parent='LogisticRegression_4d8f8ce4d6a02d8c29a0', name='labelCol', doc='label column name'): 'label',
 Param(parent='LogisticRegression_4d8f8ce4d6a02d8c29a0', name='maxIter', doc='maximum number of iterations (>= 0)'): 10,
 Param(parent='LogisticRegression_4d8f8ce4d6a02d8c29a0', name='predictionCol', doc='prediction column name'): 'prediction',
 Param(parent='LogisticRegression_4d8f8ce4d6a02d8c29a0', name='probabilityCol', doc='Column name for predicted class conditional probabilities. Note: Not all models output well-calibrated probability estimates! These probabilities should be treated as confidences, not precise probabilities'): 'probability',
 Param(parent='LogisticRegression_4d8f8ce4d6a02d8c29a0', name='rawPredictionCol', doc='raw prediction (a.k.a. confidence) column name'): 'rawPrediction',
 Param(parent='LogisticRegression_4d8f8ce4d6a02d8c29a0', name='regParam', doc='regularization parameter (>= 0)'): 0.01,
 Param(parent='LogisticRegression_4d8f8ce4d6a02d8c29a0', name='standardization', doc='whether to standardize the training features before fitting the model'): True,
 Param(parent='LogisticRegression_4d8f8ce4d6a02d8c29a0', name='threshold', doc='threshold in binary classification prediction, in range [0, 1]'): 0.5,
 Param(parent='LogisticRegression_4d8f8ce4d6a02d8c29a0', name='tol', doc='the convergence tolerance for iterative algorithms (>= 0)'): 1e-06}

FAQ

RDD to beztypowa, rozproszona kolekcja, którą można przekształcić za pomocą mapy i filtra. DataFrame dodaje nazwany schemat i przechodzi przez optymalizator Catalyst, dzięki czemu wyszukiwanie danych strukturalnych jest zazwyczaj szybsze i łatwiejsze.

Spark Wymagana wersja 2.x Java Tylko rok 8. Spark 3.x i 4.x działają na Java 8, 11 i 17 oraz wsparcie Python 3.8 w górę. Zawsze dopasowuj JDK do Spark wydanie, ponieważ nowsza wersja JDK psuje starsze kompilacje.

Asystenci AI czytają Spark Metryki interfejsu użytkownika i plany etapów służą do oznaczania nierównych partycji, szerokich przetasowań i niewykorzystanej pamięci, a następnie sugerują ponowne partycjonowanie lub łączenia rozgłoszeniowe. Skracają one prace dostrajające, które wcześniej wymagały ręcznej kontroli każdego etapu wykonania.

Copilot dobrze szkicuje szablony: rzutowania withColumn, agregacje groupBy i etapy potoku. Nazwy kolumn i klucze łączenia nadal wymagają sprawdzenia pod kątem rzeczywistego schematu, ponieważ wygenerowany kod kompiluje się bez problemu, po cichu wybierając niewłaściwe pole.

Wybierz Pandas, póki dane mieszczą się wygodnie w pamięci jednego komputera. Przejdź na PySpark gdy pliki przekraczają dostępną pamięć RAM, gdy praca musi zostać wykonana w klastrze lub gdy to samo zadanie jest przesyłane do potoku produkcyjnego, a nie do notatnika eksploracyjnego.

Py4JJavaBłąd powoduje wyjątek JVM poprzez Python most. Typowymi wyzwalaczami są brakujący lub niezgodny JDK, nieustawiony JAVA_HOME lub błąd PySpark wersja zbudowana na innej Spark wydanie niż to na ścieżce.

Zapisz skrypt jako plik .py i uruchom go za pomocą spark-submit, dodając –master, aby wybrać tryb lokalny lub klastrowy. Ten sam kod, który działa w… Jupyter działa bez zmian raz SparkSesja jest tworzona jawnie.

Transformacje takie jak „map”, „filter” i „withColumn” jedynie budują plan. Nic nie jest wykonywane, dopóki nie zostanie uruchomiona akcja taka jak „count”, „collect” lub „show”. Spark a następnie optymalizuje cały łańcuch na raz, co pozwala uniknąć zapisywania niepotrzebnych wyników pośrednich.

Podsumuj ten post następująco: