niedziela, 13 września 2015

Dataframes

Są środowiska w których pewne zjawiska występują naturalnie jak i są środowiska gdzie gdzie następuje ich już nie tak naturalne przystosowanie. Np naturę lwa jako króla Dżungli można prędzej zobaczyć w prawdziwej dżungli :

Aniżeli w sztucznie wytworzonej kępie drzew :

Inny przykład bliższy życiu informatycznemu - tzw type classes . Mają one nawet swoją stronę na wiki -> https://en.wikipedia.org/wiki/Type_class gdzie ujrzymy przykłady w Haskellu. I to właśnie on (Haskell to on?) jest naturalnym srodowiskiem dla tego zjawiska. Jednocześnie zostało ono sztucznie przystosowane do Scali

Tak to wygląda w Scali :

def min[B >: A](implicit cmp: Ordering[B]): A 
List(1,2,3,4).min
List(1,2,3,4).min(new Ordering[Int] { def compare(a: Int, b: Int) = b compare a })

A tak w haskellu :
data Ordering           =  EQ | LT | GT 
compare                 :: Ord a => a -> a -> Ordering

Mając ogromny narzut obiektowy po dekadzie spędzonej z Javą trudno mi było zrozumieć dlaczego w wielu miejscach type classes były nazywane mechanizmem funkcyjnym gdy obiektowy mózg widział w tym zawsze "wzorzec strategię!". Dopiero obcowanie z językiem naprawdę funkcyjnym pozwala uwolnić się z tej obiektowej klatki i widzieć type classes jako coś w kontekście typów danych. W sporze Rzeczowniki vs Czasowniki - Type classes będą ... przymiotnikami.

A teraz Dataframes. Dataframes zostały dodane do Sparka 1.3 i znacznie ułatwiają manipulację danymi. Chociaż można ich używać w Scali (co będziemy robić) to naturalnym środowiskiem dla Dataframes jest (chyba) język R.

Dataframes i R

Tutaj jest fajny opis teoretyczny -> What is a Data Frame

Bardzo ciekawie są one zobrazowane w kontekście "DataScience" (buzzword detected!) gdzie symbolizują serię pomiarów (rzędy) pewnej ilości czynników (kolumny). I na przykład można sobie zrobić pomiar tego jakie jest zainteresowanie warsztatem w zależności od tego co tam było :

Nazwa Tematyka Czy była pizza Szybkośc Wifi W ubikacji był papier

Normalne Dataframe

W trakcie eksperymentów okazało się, że trzeba odróżnić trochę dataframe w czystym R od dataframe w SparkowymR. I za chwile to zrobimy ale od początku.

Pierwsze prawo nauk inzynieryjskich - "Włączone do kontaktu działa lepiej"
W R taka składnia c() tworzy nam kolumnę
> column1 <- c(1,2,3,7,8,9)
> column1
[1] 1 2 3 7 8 9

I gdy takich kolumn mamy np trzy
> column2 <- c("a","b","c","x","y","z")
> column3 <- c(T,T,T,F,F,T)
> df <- data.frame(column1,column2,column3)
> df
  column1 column2 column3
1       1       a    TRUE
2       2       b    TRUE
3       3       c    TRUE
4       7       x   FALSE
5       8       y   FALSE
6       9       z    TRUE

To mamy naszego pierwszego DataFrame (kogo czego : datafrejma). I jako prawdziwi naukowcy możemy sobie pobadać co tam jest czy to w kolumnie
> df["column1"]
  column1
1       1
2       2
3       3
4       7
5       8
6       9

Czy w wierszu
> df[2,]
  column1 column2 column3
2       2       b    TRUE

Na koniec kilka ciekawych konstrukcji badawczych
> df$column2
[1] a b c x y z

> df[c("column1","column3")]
  column1 column3
1       1    TRUE
2       2    TRUE
3       3    TRUE
4       7   FALSE
5       8   FALSE
6       9    TRUE

> df[c(1,3,5),]
  column1 column2 column3
1       1       a    TRUE
3       3       c    TRUE
5       8       y   FALSE

I jedna ciekawa konstrukcja, która będzie wracała do nas raz za razem a może wydawać się trochę "dzika" w kontekście "ukrywania wszystkiego w obiekcie" bo tutaj niejako wyszczepiamy ciało obce
> filter <- df$column3==T
> df[filter,]
  column1 column2 column3
1       1       a    TRUE
2       2       b    TRUE
3       3       c    TRUE
6       9       z    TRUE

> df[df$column1 > 5,]
  column1 column2 column3
4       7       x   FALSE
5       8       y   FALSE
6       9       z    TRUE

I tak sobie działamy, i tka sobie robimy i niby to jest odpalone w Sparku i wchodzimy do SparkUI i tam nic nie ma - bo tak naprawdę to tak jak Gmoch na razie rysujemy sobie sytuacje na tablicy. Dalej będziemy działać na wbudowanym zbiorze reprezentującym drogie samochody i Fiata 128.

SparkR Dataframe

Najpierw przechodzimy ze świata czystego R do SparkR.
> sparkCars <-createDataFrame(sqlContext,mtcars)
> sparkCars
DataFrame[mpg:double, cyl:double, disp:double, hp:double, drat:double, wt:double, qsec:double, vs:double, am:double, gear:double, carb:double]

I już jest trochę inaczej bo Dataframe nie wyświetlił nam zawartości a jedynie listę kolumn. Datafrejm datafrejmowi nie równy.
Trzeba to zrobić tak :
> showDF(sparkCars)
+----+---+-----+-----+----+-----+-----+---+---+----+----+
| mpg|cyl| disp|   hp|drat|   wt| qsec| vs| am|gear|carb|
+----+---+-----+-----+----+-----+-----+---+---+----+----+
|21.0|6.0|160.0|110.0| 3.9| 2.62|16.46|0.0|1.0| 4.0| 4.0|
|21.0|6.0|160.0|110.0| 3.9|2.875|17.02|0.0|1.0| 4.0| 4.0|
|22.8|4.0|108.0| 93.0|3.85| 2.32|18.61|1.0|1.0| 4.0| 1.0|
|21.4|6.0|258.0|110.0|3.08|3.215|19.44|1.0|0.0| 3.0| 1.0|
|18.7|8.0|360.0|175.0|3.15| 3.44|17.02|0.0|0.0| 3.0| 2.0|
|18.1|6.0|225.0|105.0|2.76| 3.46|20.22|1.0|0.0| 3.0| 1.0|
|14.3|8.0|360.0|245.0|3.21| 3.57|15.84|0.0|0.0| 3.0| 4.0|
|24.4|4.0|146.7| 62.0|3.69| 3.19| 20.0|1.0|0.0| 4.0| 2.0|
|22.8|4.0|140.8| 95.0|3.92| 3.15| 22.9|1.0|0.0| 4.0| 2.0|
|19.2|6.0|167.6|123.0|3.92| 3.44| 18.3|1.0|0.0| 4.0| 4.0|
|17.8|6.0|167.6|123.0|3.92| 3.44| 18.9|1.0|0.0| 4.0| 4.0|
|16.4|8.0|275.8|180.0|3.07| 4.07| 17.4|0.0|0.0| 3.0| 3.0|
|17.3|8.0|275.8|180.0|3.07| 3.73| 17.6|0.0|0.0| 3.0| 3.0|
|15.2|8.0|275.8|180.0|3.07| 3.78| 18.0|0.0|0.0| 3.0| 3.0|
|10.4|8.0|472.0|205.0|2.93| 5.25|17.98|0.0|0.0| 3.0| 4.0|
|10.4|8.0|460.0|215.0| 3.0|5.424|17.82|0.0|0.0| 3.0| 4.0|
|14.7|8.0|440.0|230.0|3.23|5.345|17.42|0.0|0.0| 3.0| 4.0|
|32.4|4.0| 78.7| 66.0|4.08|  2.2|19.47|1.0|1.0| 4.0| 1.0|
|30.4|4.0| 75.7| 52.0|4.93|1.615|18.52|1.0|1.0| 4.0| 2.0|
|33.9|4.0| 71.1| 65.0|4.22|1.835| 19.9|1.0|1.0| 4.0| 1.0|
+----+---+-----+-----+----+-----+-----+---+---+----+----+
only showing top 20 rows

Jedna rzecz jest zła - w R nazwy samochodów to nie była kolumna tylko takie specjalne nazwy rzędów i te informacje straciliśmy. Poniżej sposób by to rozwiązac :

> carNames<-rownames(mtcars)
> frameWithNames<-cbind(carNames,mtcars)
> sparkCars<-createDataFrame(sqlContext,frameWithNames)
> showDF(sparkCars)
+-------------------+----+---+-----+-----+----+-----+-----+---+---+----+----+
|           carNames| mpg|cyl| disp|   hp|drat|   wt| qsec| vs| am|gear|carb|
+-------------------+----+---+-----+-----+----+-----+-----+---+---+----+----+
|          Mazda RX4|21.0|6.0|160.0|110.0| 3.9| 2.62|16.46|0.0|1.0| 4.0| 4.0|
|      Mazda RX4 Wag|21.0|6.0|160.0|110.0| 3.9|2.875|17.02|0.0|1.0| 4.0| 4.0|
|         Datsun 710|22.8|4.0|108.0| 93.0|3.85| 2.32|18.61|1.0|1.0| 4.0| 1.0|
|     Hornet 4 Drive|21.4|6.0|258.0|110.0|3.08|3.215|19.44|1.0|0.0| 3.0| 1.0|
|  Hornet Sportabout|18.7|8.0|360.0|175.0|3.15| 3.44|17.02|0.0|0.0| 3.0| 2.0|
|            Valiant|18.1|6.0|225.0|105.0|2.76| 3.46|20.22|1.0|0.0| 3.0| 1.0|
|         Duster 360|14.3|8.0|360.0|245.0|3.21| 3.57|15.84|0.0|0.0| 3.0| 4.0|
|          Merc 240D|24.4|4.0|146.7| 62.0|3.69| 3.19| 20.0|1.0|0.0| 4.0| 2.0|
|           Merc 230|22.8|4.0|140.8| 95.0|3.92| 3.15| 22.9|1.0|0.0| 4.0| 2.0|
|           Merc 280|19.2|6.0|167.6|123.0|3.92| 3.44| 18.3|1.0|0.0| 4.0| 4.0|
|          Merc 280C|17.8|6.0|167.6|123.0|3.92| 3.44| 18.9|1.0|0.0| 4.0| 4.0|
|         Merc 450SE|16.4|8.0|275.8|180.0|3.07| 4.07| 17.4|0.0|0.0| 3.0| 3.0|
|         Merc 450SL|17.3|8.0|275.8|180.0|3.07| 3.73| 17.6|0.0|0.0| 3.0| 3.0|
|        Merc 450SLC|15.2|8.0|275.8|180.0|3.07| 3.78| 18.0|0.0|0.0| 3.0| 3.0|
| Cadillac Fleetwood|10.4|8.0|472.0|205.0|2.93| 5.25|17.98|0.0|0.0| 3.0| 4.0|
|Lincoln Continental|10.4|8.0|460.0|215.0| 3.0|5.424|17.82|0.0|0.0| 3.0| 4.0|
|  Chrysler Imperial|14.7|8.0|440.0|230.0|3.23|5.345|17.42|0.0|0.0| 3.0| 4.0|
|           Fiat 128|32.4|4.0| 78.7| 66.0|4.08|  2.2|19.47|1.0|1.0| 4.0| 1.0|
|        Honda Civic|30.4|4.0| 75.7| 52.0|4.93|1.615|18.52|1.0|1.0| 4.0| 2.0|
|     Toyota Corolla|33.9|4.0| 71.1| 65.0|4.22|1.835| 19.9|1.0|1.0| 4.0| 1.0|
+-------------------+----+---+-----+-----+----+-----+-----+---+---+----+----+

Czu jest prostszy sposób? Być może jest.


W spark UI pojawiają się już jakieś wpisy i nawet jakieś dedykowane dla R - RRDD

I też niestety rzecz nie zawesoła. RDD to poziom abstrakcji niżej aniżeli Dataframes i to na poziomie RDD mamy monitoring. Jest to pewien generator wyzwań i miejmy nadzieję, że w przyszłych odsłonach pojawi się dedykowany monitoring Dataframe.

Tutaj jest dokumentacja --> https://spark.apache.org/docs/latest/api/R/index.html i można się pobawić.

> take(sparkCars,3)
       carNames  mpg cyl disp  hp drat    wt  qsec vs am gear carb
1     Mazda RX4 21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
2 Mazda RX4 Wag 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
3    Datsun 710 22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
> head(sparkCars)
           carNames  mpg cyl disp  hp drat    wt  qsec vs am gear carb
1         Mazda RX4 21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
2     Mazda RX4 Wag 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
3        Datsun 710 22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
4    Hornet 4 Drive 21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
5 Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
6           Valiant 18.1   6  225 105 2.76 3.460 20.22  1  0    3    1

Najpierw trochę lansu

 stacMnieNaOC<-sparkCars[sparkCars$cyl > 6]

> showDF(stacMnieNaOC)
+-------------------+----+---+-----+-----+----+-----+-----+---+---+----+----+
|           carNames| mpg|cyl| disp|   hp|drat|   wt| qsec| vs| am|gear|carb|
+-------------------+----+---+-----+-----+----+-----+-----+---+---+----+----+
|  Hornet Sportabout|18.7|8.0|360.0|175.0|3.15| 3.44|17.02|0.0|0.0| 3.0| 2.0|
|         Duster 360|14.3|8.0|360.0|245.0|3.21| 3.57|15.84|0.0|0.0| 3.0| 4.0|
|         Merc 450SE|16.4|8.0|275.8|180.0|3.07| 4.07| 17.4|0.0|0.0| 3.0| 3.0|
|         Merc 450SL|17.3|8.0|275.8|180.0|3.07| 3.73| 17.6|0.0|0.0| 3.0| 3.0|
|        Merc 450SLC|15.2|8.0|275.8|180.0|3.07| 3.78| 18.0|0.0|0.0| 3.0| 3.0|
| Cadillac Fleetwood|10.4|8.0|472.0|205.0|2.93| 5.25|17.98|0.0|0.0| 3.0| 4.0|
|Lincoln Continental|10.4|8.0|460.0|215.0| 3.0|5.424|17.82|0.0|0.0| 3.0| 4.0|
|  Chrysler Imperial|14.7|8.0|440.0|230.0|3.23|5.345|17.42|0.0|0.0| 3.0| 4.0|
|   Dodge Challenger|15.5|8.0|318.0|150.0|2.76| 3.52|16.87|0.0|0.0| 3.0| 2.0|
|        AMC Javelin|15.2|8.0|304.0|150.0|3.15|3.435| 17.3|0.0|0.0| 3.0| 2.0|
|         Camaro Z28|13.3|8.0|350.0|245.0|3.73| 3.84|15.41|0.0|0.0| 3.0| 4.0|
|   Pontiac Firebird|19.2|8.0|400.0|175.0|3.08|3.845|17.05|0.0|0.0| 3.0| 2.0|
|     Ford Pantera L|15.8|8.0|351.0|264.0|4.22| 3.17| 14.5|0.0|1.0| 5.0| 4.0|
|      Maserati Bora|15.0|8.0|301.0|335.0|3.54| 3.57| 14.6|0.0|1.0| 5.0| 8.0|

I średnia po hp

> mhp<-mean(stacMnieNaOC$hp)
> mhp
Column avg(hp) 
> showDF(select(stacMnieNaOC,mhp))
+------------------+
|           avg(hp)|
+------------------+
|209.21428571428572|
+------------------+

I jeszcze taka interesująca rzecz związxana pośrednio z dataframe:

Project tungsten ---> https://databricks.com/blog/2015/04/28/project-tungsten-bringing-spark-closer-to-bare-metal.html --- z tego co pamiętam oni chcą zrobić własny wyspecjalizowany mechanizm do zarządzania pamięcią -(haha szach mat cplusplusowcy - ...chyba)

I jeszcze taki patent :

> registerTempTable(sparkCars,"cars")
> showDF(sql(sqlContext,"select cyl,AVG(hp) as averageHP FROM cars GROUP BY cyl"))
+---+------------------+
|cyl|         averageHP|
+---+------------------+
|6.0|122.28571428571429|
|8.0|209.21428571428572|
|4.0| 82.63636363636364|
+---+------------------+

I na koniec zapiszmy daframe jako json :

> write.df(sparkCars,"/tmp/cars.json","json")
pawel@maszyna:/tmp$ cat cars.json
cat: cars.json: Is a directory
pawel@maszyna:/tmp$ ls -al cars.json
total 60
drwxrwxr-x  2 pawel pawel  4096 wrz 13 19:10 .
drwxrwxrwt 27 root  root  36864 wrz 13 19:10 ..
-rw-r--r--  1 pawel pawel  4709 wrz 13 19:10 part-r-00000-4767f3fc-8fc8-412c-8eb7-fd56c25686e0
-rw-rw-r--  1 pawel pawel    48 wrz 13 19:10 .part-r-00000-4767f3fc-8fc8-412c-8eb7-fd56c25686e0.crc
-rw-r--r--  1 pawel pawel     0 wrz 13 19:10 _SUCCESS
-rw-rw-r--  1 pawel pawel     8 wrz 13 19:10 ._SUCCESS.crc

No trzeba będzie and tym trochę popracować...

Spark Scala Dataframe

/bin/sparks/spark-1.5.0-bin-hadoop2.6$ bin/spark-shell 
scala> val frame=sqlContext.read.json("/tmp/cars.json")
frame: org.apache.spark.sql.DataFrame = [am: double, carNames: string, carb: double, cyl: double, disp: double, drat: double, gear: double, hp: double, mpg: double, qsec: double, vs: double, wt: double]

scala> frame.show()
+---+-------------------+----+---+-----+----+----+-----+----+-----+---+-----+
| am|           carNames|carb|cyl| disp|drat|gear|   hp| mpg| qsec| vs|   wt|
+---+-------------------+----+---+-----+----+----+-----+----+-----+---+-----+
|1.0|          Mazda RX4| 4.0|6.0|160.0| 3.9| 4.0|110.0|21.0|16.46|0.0| 2.62|
|1.0|      Mazda RX4 Wag| 4.0|6.0|160.0| 3.9| 4.0|110.0|21.0|17.02|0.0|2.875|
|1.0|         Datsun 710| 1.0|4.0|108.0|3.85| 4.0| 93.0|22.8|18.61|1.0| 2.32|
|0.0|     Hornet 4 Drive| 1.0|6.0|258.0|3.08| 3.0|110.0|21.4|19.44|1.0|3.215|
|0.0|  Hornet Sportabout| 2.0|8.0|360.0|3.15| 3.0|175.0|18.7|17.02|0.0| 3.44|
|0.0|            Valiant| 1.0|6.0|225.0|2.76| 3.0|105.0|18.1|20.22|1.0| 3.46|
|0.0|         Duster 360| 4.0|8.0|360.0|3.21| 3.0|245.0|14.3|15.84|0.0| 3.57|
|0.0|          Merc 240D| 2.0|4.0|146.7|3.69| 4.0| 62.0|24.4| 20.0|1.0| 3.19|
|0.0|           Merc 230| 2.0|4.0|140.8|3.92| 4.0| 95.0|22.8| 22.9|1.0| 3.15|
|0.0|           Merc 280| 4.0|6.0|167.6|3.92| 4.0|123.0|19.2| 18.3|1.0| 3.44|
|0.0|          Merc 280C| 4.0|6.0|167.6|3.92| 4.0|123.0|17.8| 18.9|1.0| 3.44|
|0.0|         Merc 450SE| 3.0|8.0|275.8|3.07| 3.0|180.0|16.4| 17.4|0.0| 4.07|
|0.0|         Merc 450SL| 3.0|8.0|275.8|3.07| 3.0|180.0|17.3| 17.6|0.0| 3.73|
|0.0|        Merc 450SLC| 3.0|8.0|275.8|3.07| 3.0|180.0|15.2| 18.0|0.0| 3.78|
|0.0| Cadillac Fleetwood| 4.0|8.0|472.0|2.93| 3.0|205.0|10.4|17.98|0.0| 5.25|
|0.0|Lincoln Continental| 4.0|8.0|460.0| 3.0| 3.0|215.0|10.4|17.82|0.0|5.424|
|0.0|  Chrysler Imperial| 4.0|8.0|440.0|3.23| 3.0|230.0|14.7|17.42|0.0|5.345|
|1.0|           Fiat 128| 1.0|4.0| 78.7|4.08| 4.0| 66.0|32.4|19.47|1.0|  2.2|
|1.0|        Honda Civic| 2.0|4.0| 75.7|4.93| 4.0| 52.0|30.4|18.52|1.0|1.615|
|1.0|     Toyota Corolla| 1.0|4.0| 71.1|4.22| 4.0| 65.0|33.9| 19.9|1.0|1.835|
+---+-------------------+----+---+-----+----+----+-----+----+-----+---+-----+

Co to za "am"? Nie wiadomo - wywalamy

scala> val cleaned=frame.drop("am")
cleaned: org.apache.spark.sql.DataFrame = [carNames: string, carb: double, cyl: double, disp: double, drat: double, gear: double, hp: double, mpg: double, qsec: double, vs: double, wt: double]

scala> cleaned.show(2)
+-------------+----+---+-----+----+----+-----+----+-----+---+-----+
|     carNames|carb|cyl| disp|drat|gear|   hp| mpg| qsec| vs|   wt|
+-------------+----+---+-----+----+----+-----+----+-----+---+-----+
|    Mazda RX4| 4.0|6.0|160.0| 3.9| 4.0|110.0|21.0|16.46|0.0| 2.62|
|Mazda RX4 Wag| 4.0|6.0|160.0| 3.9| 4.0|110.0|21.0|17.02|0.0|2.875|
+-------------+----+---+-----+----+----+-----+----+-----+---+-----+
only showing top 2 rows

I zróbmy podobne ćwiczenie do poprzedniego

scala> cleaned.filter(cleaned("cyl")>6)
res2: org.apache.spark.sql.DataFrame = [carNames: string, carb: double, cyl: double, disp: double, drat: double, gear: double, hp: double, mpg: double, qsec: double, vs: double, wt: double]

scala> cleaned.filter(cleaned("cyl")>6).show(3)
+-----------------+----+---+-----+----+----+-----+----+-----+---+----+
|         carNames|carb|cyl| disp|drat|gear|   hp| mpg| qsec| vs|  wt|
+-----------------+----+---+-----+----+----+-----+----+-----+---+----+
|Hornet Sportabout| 2.0|8.0|360.0|3.15| 3.0|175.0|18.7|17.02|0.0|3.44|
|       Duster 360| 4.0|8.0|360.0|3.21| 3.0|245.0|14.3|15.84|0.0|3.57|
|       Merc 450SE| 3.0|8.0|275.8|3.07| 3.0|180.0|16.4| 17.4|0.0|4.07|
+-----------------+----+---+-----+----+----+-----+----+-----+---+----+
only showing top 3 rows


scala> val duzoCylindrow=cleaned.filter(cleaned("cyl")>6)
duzoCylindrow: org.apache.spark.sql.DataFrame = [carNames: string, carb: double, cyl: double, disp: double, drat: double, gear: double, hp: double, mpg: double, qsec: double, vs: double, wt: double]

I jedziemy ze średnią

scala> import org.apache.spark.sql.functions.avg
import org.apache.spark.sql.functions.avg

scala> cleaned.agg(avg(cleaned("hp"))).show()
+--------+
| avg(hp)|
+--------+
|146.6875|
+--------+

Na koniec stwórzmy sobie dataframe "ręcznie" - to bardzo ale to bardzo przydaje się przy testowaniu jak trzeba sobie małe DF pod testy skonstruować.

scala> case class CarRow(name:String,price:Int)

scala> val rows=Seq(CarRow("Fiat 128",1000), CarRow("Merc 230",4000))
rows: Seq[CarRow] = List(CarRow(Fiat 128,1000), CarRow(Merc 230,4000))

scala> val smallDF=sqlContext.createDataFrame(rows)
smallDF: org.apache.spark.sql.DataFrame = [name: string, price: int]

scala> smallDF.show
+--------+-----+
|    name|price|
+--------+-----+
|Fiat 128| 1000|
|Merc 230| 4000|
+--------+-----+

scala> cleaned.join(smallDF,cleaned("carNames") === smallDF("name") ).show()
+--------+----+---+-----+----+----+----+----+-----+---+----+--------+-----+
|carNames|carb|cyl| disp|drat|gear|  hp| mpg| qsec| vs|  wt|    name|price|
+--------+----+---+-----+----+----+----+----+-----+---+----+--------+-----+
|Merc 230| 2.0|4.0|140.8|3.92| 4.0|95.0|22.8| 22.9|1.0|3.15|Merc 230| 4000|
|Fiat 128| 1.0|4.0| 78.7|4.08| 4.0|66.0|32.4|19.47|1.0| 2.2|Fiat 128| 1000|
+--------+----+---+-----+----+----+----+----+-----+---+----+--------+-----+

No czyli dużo ciekawych rzeczy nas czeka

Warsztaty

Za tydzień pierwsze warsztaty ze Sparka --> http://www.meetup.com/Java-User-Group-Lodz/events/225008470/, następnie trochę rzeczy bardziej zaawansowanych i dopiero przechodzimy do dataframów (listopad). Dlaczego tak?

Jestem zdania, że trzeba chociaż trochę poznać bebechy zanim zacznie się skakać po wyższych warstwach abstrakcji. Weźmy na przykład taki Hibernate.

Z perspektywy czasu wiem, że najlepiej poznać i zrozumieć model relacyjny danych. Dobrze zrozumieć OOP i wtedy i dopiero wtedy jak się znajdzie akurat w takiej sytuacji, że z powodów takich a nie innych trzeba jeden model połączyć z drugim to można się pokusić o zastosowanie gotowca jakim jest hibernate.

Z bolesnego doświadczenia wiem, że zazwyczaj jednak Hibernate używa się jako zastępnik zrozumienia modelu relacyjnego co koniec końców bardzo boli.
Bo tak się zachowuje Hibernate na pocżątku projektu gdy mamy dwie tabele i jednego selekta

a tak to najczęściej wygląda później

Także najpierw zrozumiemy jak np. lambdy sa rozsyłane po clustrze aby nie zserialiować w paczce połowy systemu - i takie tam różne ciekawostki.

Plus jest jeszcze fajna strona z darmowymi kursami BigData gdzie pracuje się na przygotowanych obrazach. --> http://bigdatauniversity.com/