Datenexploration von Web-Analytics-Daten mit Spark

Data-Driven Marketing: Wie die kundenzentrierte Ansprache in Zukunft gelingt, Teil 2

Zur Blog-Serie https://www.shi-gmbh.com/herausforderung-customer-centricity/

Inhalt

  1. Vorbereitung und einlesen der Daten
  2. Datenstruktur
  3. Deskriptive Analyse - Beispiele
    • 3.1. Device
    • 3.2. GeoNetwork
    • 3.3. Traffic Source
    • 3.4. Channel Grouping
    • 3.5. VisitNumber
    • 3.6. Date
    • 3.7. FullVisitorId - Besucherprofile
  4. Webanalyitcs - Hits und Metriken
    • 4.1. Beliebteste Landing Pages, explode-Beispiel
    • 4.2. Bounce Rate
    • 4.3. Churn Analysis
    • 4.4. Schlüsselmerkmale von Bescuhen für das Revenue

1. Vorbereitung und einlesen der Daten

Damit jupyter den lokal installierten Spark mit Python nutzen kann, benutzen wir das Modul findspark. Ausserdem werden die pySpark-Module importiert, die für die Analyse benötigt werden.

In [3]:
import findspark
findspark.init('/usr/local/Cellar/apache-spark/2.3.2/libexec/')

import pyspark
from pyspark.sql import SparkSession
from pyspark.sql.functions import *

Wir starten die Spark Applikation indem wir eine SparkSession erzeugen

In [4]:
sc = SparkSession.builder.appName("TestGA").getOrCreate()

Wir werden die Daten in einem sog. DataFrame reinladen, der im wesentlichen einer Tabelle mit benannten Spalten entspricht und SQL-ähnliche Abfragen der Daten emöglicht. DataFrames lassen sich für parallele Berechnungen ähnlich wie mit der grundlegenden Datenstruktur in Spark, sog. RDDs, auf einem Cluster gut verteilen, bieten aber eben den Vorteil einer Schnittstelle, die jedem, der mit einer klassischen Datenbank gearbeitet hat, bekannt vorkommen dürfte. Dank eines Query-Optimizer im Hintergrund, Catalyst, lassen sich Dataframes sehr effizient abfragen und transformieren.

Die mit gzip kompirmierten JSON-Daten lassen sich direkt einlesen, mit dem Stern-Operator im Pfad lassen sich mehrere Dateien in den selben Dataframe reinladen

In [5]:
dataF = sc.read.json('/Users/eduardoschumann/Documents/Projekte/E-CommerceAnalytics/ga_data/ga_sessions_2016*.json.gz')

2. Datenstruktur

In [6]:
dataF.count(), len(dataF.columns)
Out[6]:
(436393, 13)

Wir haben grob die Hälfte der Daten reingeladen, der gesamte Datensatz hat (903653, 13) Zeilen / Spalten. Jede Zeile stellt eine Sitzung dar, in der ein Benutzer den Store besucht. Wir können uns das Schema anzeigen lassen, um mehr über die Spalten zu erfahren.

In [7]:
dataF.printSchema()
root
 |-- channelGrouping: string (nullable = true)
 |-- customDimensions: array (nullable = true)
 |    |-- element: struct (containsNull = true)
 |    |    |-- index: string (nullable = true)
 |    |    |-- value: string (nullable = true)
 |-- date: string (nullable = true)
 |-- device: struct (nullable = true)
 |    |-- browser: string (nullable = true)
 |    |-- browserSize: string (nullable = true)
 |    |-- browserVersion: string (nullable = true)
 |    |-- deviceCategory: string (nullable = true)
 |    |-- flashVersion: string (nullable = true)
 |    |-- isMobile: boolean (nullable = true)
 |    |-- language: string (nullable = true)
 |    |-- mobileDeviceBranding: string (nullable = true)
 |    |-- mobileDeviceInfo: string (nullable = true)
 |    |-- mobileDeviceMarketingName: string (nullable = true)
 |    |-- mobileDeviceModel: string (nullable = true)
 |    |-- mobileInputSelector: string (nullable = true)
 |    |-- operatingSystem: string (nullable = true)
 |    |-- operatingSystemVersion: string (nullable = true)
 |    |-- screenColors: string (nullable = true)
 |    |-- screenResolution: string (nullable = true)
 |-- fullVisitorId: string (nullable = true)
 |-- geoNetwork: struct (nullable = true)
 |    |-- city: string (nullable = true)
 |    |-- cityId: string (nullable = true)
 |    |-- continent: string (nullable = true)
 |    |-- country: string (nullable = true)
 |    |-- latitude: string (nullable = true)
 |    |-- longitude: string (nullable = true)
 |    |-- metro: string (nullable = true)
 |    |-- networkDomain: string (nullable = true)
 |    |-- networkLocation: string (nullable = true)
 |    |-- region: string (nullable = true)
 |    |-- subContinent: string (nullable = true)
 |-- hits: array (nullable = true)
 |    |-- element: struct (containsNull = true)
 |    |    |-- appInfo: struct (nullable = true)
 |    |    |    |-- exitScreenName: string (nullable = true)
 |    |    |    |-- landingScreenName: string (nullable = true)
 |    |    |    |-- screenDepth: string (nullable = true)
 |    |    |    |-- screenName: string (nullable = true)
 |    |    |-- contentGroup: struct (nullable = true)
 |    |    |    |-- contentGroup1: string (nullable = true)
 |    |    |    |-- contentGroup2: string (nullable = true)
 |    |    |    |-- contentGroup3: string (nullable = true)
 |    |    |    |-- contentGroup4: string (nullable = true)
 |    |    |    |-- contentGroup5: string (nullable = true)
 |    |    |    |-- contentGroupUniqueViews1: string (nullable = true)
 |    |    |    |-- contentGroupUniqueViews2: string (nullable = true)
 |    |    |    |-- contentGroupUniqueViews3: string (nullable = true)
 |    |    |    |-- previousContentGroup1: string (nullable = true)
 |    |    |    |-- previousContentGroup2: string (nullable = true)
 |    |    |    |-- previousContentGroup3: string (nullable = true)
 |    |    |    |-- previousContentGroup4: string (nullable = true)
 |    |    |    |-- previousContentGroup5: string (nullable = true)
 |    |    |-- customDimensions: array (nullable = true)
 |    |    |    |-- element: string (containsNull = true)
 |    |    |-- customMetrics: array (nullable = true)
 |    |    |    |-- element: string (containsNull = true)
 |    |    |-- customVariables: array (nullable = true)
 |    |    |    |-- element: string (containsNull = true)
 |    |    |-- eCommerceAction: struct (nullable = true)
 |    |    |    |-- action_type: string (nullable = true)
 |    |    |    |-- option: string (nullable = true)
 |    |    |    |-- step: string (nullable = true)
 |    |    |-- eventInfo: struct (nullable = true)
 |    |    |    |-- eventAction: string (nullable = true)
 |    |    |    |-- eventCategory: string (nullable = true)
 |    |    |    |-- eventLabel: string (nullable = true)
 |    |    |-- exceptionInfo: struct (nullable = true)
 |    |    |    |-- isFatal: boolean (nullable = true)
 |    |    |-- experiment: array (nullable = true)
 |    |    |    |-- element: string (containsNull = true)
 |    |    |-- hitNumber: string (nullable = true)
 |    |    |-- hour: string (nullable = true)
 |    |    |-- isEntrance: boolean (nullable = true)
 |    |    |-- isExit: boolean (nullable = true)
 |    |    |-- isInteraction: boolean (nullable = true)
 |    |    |-- item: struct (nullable = true)
 |    |    |    |-- currencyCode: string (nullable = true)
 |    |    |    |-- transactionId: string (nullable = true)
 |    |    |-- latencyTracking: struct (nullable = true)
 |    |    |    |-- domContentLoadedTime: string (nullable = true)
 |    |    |    |-- domInteractiveTime: string (nullable = true)
 |    |    |    |-- domLatencyMetricsSample: string (nullable = true)
 |    |    |    |-- domainLookupTime: string (nullable = true)
 |    |    |    |-- pageDownloadTime: string (nullable = true)
 |    |    |    |-- pageLoadSample: string (nullable = true)
 |    |    |    |-- pageLoadTime: string (nullable = true)
 |    |    |    |-- redirectionTime: string (nullable = true)
 |    |    |    |-- serverConnectionTime: string (nullable = true)
 |    |    |    |-- serverResponseTime: string (nullable = true)
 |    |    |    |-- speedMetricsSample: string (nullable = true)
 |    |    |-- minute: string (nullable = true)
 |    |    |-- page: struct (nullable = true)
 |    |    |    |-- hostname: string (nullable = true)
 |    |    |    |-- pagePath: string (nullable = true)
 |    |    |    |-- pagePathLevel1: string (nullable = true)
 |    |    |    |-- pagePathLevel2: string (nullable = true)
 |    |    |    |-- pagePathLevel3: string (nullable = true)
 |    |    |    |-- pagePathLevel4: string (nullable = true)
 |    |    |    |-- pageTitle: string (nullable = true)
 |    |    |    |-- searchCategory: string (nullable = true)
 |    |    |    |-- searchKeyword: string (nullable = true)
 |    |    |-- product: array (nullable = true)
 |    |    |    |-- element: struct (containsNull = true)
 |    |    |    |    |-- customDimensions: array (nullable = true)
 |    |    |    |    |    |-- element: string (containsNull = true)
 |    |    |    |    |-- customMetrics: array (nullable = true)
 |    |    |    |    |    |-- element: string (containsNull = true)
 |    |    |    |    |-- isClick: boolean (nullable = true)
 |    |    |    |    |-- isImpression: boolean (nullable = true)
 |    |    |    |    |-- localProductPrice: string (nullable = true)
 |    |    |    |    |-- localProductRevenue: string (nullable = true)
 |    |    |    |    |-- productBrand: string (nullable = true)
 |    |    |    |    |-- productListName: string (nullable = true)
 |    |    |    |    |-- productListPosition: string (nullable = true)
 |    |    |    |    |-- productPrice: string (nullable = true)
 |    |    |    |    |-- productQuantity: string (nullable = true)
 |    |    |    |    |-- productRevenue: string (nullable = true)
 |    |    |    |    |-- productSKU: string (nullable = true)
 |    |    |    |    |-- productVariant: string (nullable = true)
 |    |    |    |    |-- v2ProductCategory: string (nullable = true)
 |    |    |    |    |-- v2ProductName: string (nullable = true)
 |    |    |-- promotion: array (nullable = true)
 |    |    |    |-- element: struct (containsNull = true)
 |    |    |    |    |-- promoCreative: string (nullable = true)
 |    |    |    |    |-- promoId: string (nullable = true)
 |    |    |    |    |-- promoName: string (nullable = true)
 |    |    |    |    |-- promoPosition: string (nullable = true)
 |    |    |-- promotionActionInfo: struct (nullable = true)
 |    |    |    |-- promoIsClick: boolean (nullable = true)
 |    |    |    |-- promoIsView: boolean (nullable = true)
 |    |    |-- publisher_infos: array (nullable = true)
 |    |    |    |-- element: string (containsNull = true)
 |    |    |-- referer: string (nullable = true)
 |    |    |-- social: struct (nullable = true)
 |    |    |    |-- hasSocialSourceReferral: string (nullable = true)
 |    |    |    |-- socialInteractionNetworkAction: string (nullable = true)
 |    |    |    |-- socialNetwork: string (nullable = true)
 |    |    |-- time: string (nullable = true)
 |    |    |-- transaction: struct (nullable = true)
 |    |    |    |-- affiliation: string (nullable = true)
 |    |    |    |-- currencyCode: string (nullable = true)
 |    |    |    |-- localTransactionRevenue: string (nullable = true)
 |    |    |    |-- localTransactionShipping: string (nullable = true)
 |    |    |    |-- localTransactionTax: string (nullable = true)
 |    |    |    |-- transactionCoupon: string (nullable = true)
 |    |    |    |-- transactionId: string (nullable = true)
 |    |    |    |-- transactionRevenue: string (nullable = true)
 |    |    |    |-- transactionShipping: string (nullable = true)
 |    |    |    |-- transactionTax: string (nullable = true)
 |    |    |-- type: string (nullable = true)
 |-- socialEngagementType: string (nullable = true)
 |-- totals: struct (nullable = true)
 |    |-- bounces: string (nullable = true)
 |    |-- hits: string (nullable = true)
 |    |-- newVisits: string (nullable = true)
 |    |-- pageviews: string (nullable = true)
 |    |-- timeOnSite: string (nullable = true)
 |    |-- totalTransactionRevenue: string (nullable = true)
 |    |-- transactionRevenue: string (nullable = true)
 |    |-- transactions: string (nullable = true)
 |    |-- visits: string (nullable = true)
 |-- trafficSource: struct (nullable = true)
 |    |-- adContent: string (nullable = true)
 |    |-- adwordsClickInfo: struct (nullable = true)
 |    |    |-- adNetworkType: string (nullable = true)
 |    |    |-- criteriaParameters: string (nullable = true)
 |    |    |-- gclId: string (nullable = true)
 |    |    |-- isVideoAd: boolean (nullable = true)
 |    |    |-- page: string (nullable = true)
 |    |    |-- slot: string (nullable = true)
 |    |-- campaign: string (nullable = true)
 |    |-- campaignCode: string (nullable = true)
 |    |-- isTrueDirect: boolean (nullable = true)
 |    |-- keyword: string (nullable = true)
 |    |-- medium: string (nullable = true)
 |    |-- referralPath: string (nullable = true)
 |    |-- source: string (nullable = true)
 |-- visitId: string (nullable = true)
 |-- visitNumber: string (nullable = true)
 |-- visitStartTime: string (nullable = true)

Erläuterung Schema

Als erstes fällt auf, dass die meisten der dreizehn verschiedenen Spalten, die Eigenschaften des Besuchs erfassen, ihrerseits eingebettete Strukturen mit einem eigenen Unterschema haben. Außerdem werden alle Werte als String eingelesen.

Einfache Spalten mit einzelnen Werten lassen sich leicht von Namen her interpretieren: fullVisitorId, date, visitId, visitStartTime, visitNumber

Die meisten Spalten haben jedoch Unterspalten (Typ struct), um verwandte Merkmale zusammenzufassen, so z.B. die Spalte device mit Unterspalten für Merkmale des für den Besuch verwendenten Geräts. Verschiedene geographischen Informationen werden unter geoNetwork gesammelt, Details zur Herkunft des Besuchs unter trafficSource.

Die Folge der im Prinzip beliebig vielen Interaktionen innerhalb eines Besuchs werden in der Spalte hits gesammelt, die dafür von Typ array ist. Jedes Element in dieser Liste von hits ist wiederum ein Objekt mit einem festen, umfangreichen Schema, um damit zusammenhängende Informationen zu erfassen, etwa die besuchte Seite (page), vordefinierte Content-Gruppen (content), angezeigte Produkte (product), getätigte Transaktionen bzw. Käufe (transaction), usw.

Die Spalte totals fasst verschiedene Statistiken über die hits des Besuchs zusammen: Anzahl, die Gesamteinnahmen aller Transaktionen (totalTransactionRevenue), Anzahl der besuchten Seiten, usw.

Spark bietet verschiedene Funktionen, um den Datentyp einer Spalte anzupassen und mit den verschiedenen Arten von Verschachtelungen in einem DataFrame umzugehen. Damit können Abfragen ausgewertet, ohne dass dafür die Daten vorher denormalisiert werden müssen, wie wir bei der weiteren Erkundung sehen werden.

3. Deskriptive Analyse - Beispiele

Arbeiten mit DataFrames

Spark stellt eine funktionellen Sprache zu Verfügung, die sich an SQL orientiert, um ein DataFrame abzufragen. Histogramme bzw. Frequenzen für die Werte in einer Spalte lassen sich damit analog zu SQL berechnen.

In [8]:
dataF.groupBy('channelGrouping').count().orderBy("count").show()
+---------------+------+
|channelGrouping| count|
+---------------+------+
|        (Other)|     6|
|        Display|  2958|
|     Affiliates|  6642|
|    Paid Search| 11076|
|       Referral| 46007|
|         Direct| 56597|
| Organic Search|147126|
|         Social|165981|
+---------------+------+

Eingebaute Funktionen wie describe() berechnen grundlegende Verteilungs- / Streuungsparameter. Im Beispiel sieht man wie man auf die Unterspalten von totals zugreifen kann.

In [9]:
dataF.select('totals.hits', 'totals.timeOnSite', 'totals.transactions').describe().show()
+-------+------------------+------------------+-------------------+
|summary|              hits|        timeOnSite|       transactions|
+-------+------------------+------------------+-------------------+
|  count|            436393|            218901|               5201|
|   mean| 4.771146649923349|243.96800836908008| 1.0515285522014997|
| stddev|10.626958855819973| 476.8294417539209|0.34311647159360587|
|    min|                 1|                 1|                  1|
|    max|                99|               999|                  8|
+-------+------------------+------------------+-------------------+

Zeilen können mit show ausgegeben werden.

In [10]:
dataF.select('trafficSource').show(5, truncate=False)
+-------------------------------------------------------------------------------------------------------------------------+
|trafficSource                                                                                                            |
+-------------------------------------------------------------------------------------------------------------------------+
|[, [, not available in demo dataset,,,,], (not set),,, (not provided), organic,, google]                                 |
|[, [, not available in demo dataset,,,,], (not set),,, (not provided), organic,, google]                                 |
|[, [, not available in demo dataset,,,,], (not set),,, (not provided), organic,, google]                                 |
|[, [, not available in demo dataset,,,,], (not set),, true,, (none),, (direct)]                                          |
|[, [, not available in demo dataset,,,,], (not set),,,, referral, /2015/03/11/google-merch-store-new-url/, phandroid.com]|
+-------------------------------------------------------------------------------------------------------------------------+
only showing top 5 rows

In [11]:
dataF.show(10)
+---------------+--------------------+--------+--------------------+-------------------+--------------------+--------------------+--------------------+--------------------+--------------------+----------+-----------+--------------+
|channelGrouping|    customDimensions|    date|              device|      fullVisitorId|          geoNetwork|                hits|socialEngagementType|              totals|       trafficSource|   visitId|visitNumber|visitStartTime|
+---------------+--------------------+--------+--------------------+-------------------+--------------------+--------------------+--------------------+--------------------+--------------------+----------+-----------+--------------+
| Organic Search|         [[4, APAC]]|20160831|[Chrome, not avai...|5478623230383885328|[(not set), not a...|[[[www.googlemerc...|Not Socially Engaged|[, 6, 1, 6, 148,,...|[, [, not availab...|1472632459|          1|    1472632459|
| Organic Search|         [[4, EMEA]]|20160831|[Chrome, not avai...|9612697236886680399|[not available in...|[[[shop.googlemer...|Not Socially Engaged|[, 13,, 13, 421,,...|[, [, not availab...|1472643849|          4|    1472643849|
| Organic Search|         [[4, EMEA]]|20160831|[Internet Explore...|0837658092652033256|[London, not avai...|[[[shop.googlemer...|Not Socially Engaged|[, 4, 1, 4, 1422,...|[, [, not availab...|1472659695|          1|    1472659695|
|         Direct|         [[4, EMEA]]|20160831|[Chrome, not avai...|6532055062759933865|[not available in...|[[[shop.googlemer...|Not Socially Engaged|[, 5,, 5, 1156,,,...|[, [, not availab...|1472648364|         23|    1472648364|
|       Referral|         [[4, EMEA]]|20160831|[Chrome, not avai...|0040335064426021006|[not available in...|[[[shop.googlemer...|Not Socially Engaged|[, 5, 1, 5, 135,,...|[, [, not availab...|1472659563|          1|    1472659563|
| Organic Search|         [[4, EMEA]]|20160831|[Chrome, not avai...|7977143020316134741|[London, not avai...|[[[shop.googlemer...|Not Socially Engaged|[, 7,, 7, 142,,,, 1]|[, [, not availab...|1472650713|          2|    1472650713|
| Organic Search|[[4, North America]]|20160831|[Chrome, not avai...|0703987475057178830|[not available in...|[[[shop.googlemer...|Not Socially Engaged|[, 7, 1, 3, 26,,,...|[, [, not availab...|1472702236|          1|    1472702236|
| Organic Search|[[4, North America]]|20160831|[Chrome, not avai...|8501295234755313921|[not available in...|[[[shop.googlemer...|Not Socially Engaged|[, 10, 1, 4, 54,,...|[, [, not availab...|1472666162|          1|    1472666162|
|         Direct|                  []|20160831|[Safari, not avai...|5407384644549670219|[Bangkok, not ava...|[[[shop.googlemer...|Not Socially Engaged|[, 11, 1, 10, 839...|[, [, not availab...|1472697195|          1|    1472697195|
| Organic Search|[[4, Central Amer...|20160831|[Chrome, not avai...|9740144896864972546|[not available in...|[[[shop.googlemer...|Not Socially Engaged|[, 11, 1, 11, 764...|[, [, not availab...|1472671647|          1|    1472671647|
+---------------+--------------------+--------+--------------------+-------------------+--------------------+--------------------+--------------------+--------------------+--------------------+----------+-----------+--------------+
only showing top 10 rows

Auf die Elemente aus dem Array aus der Spalte Hits kann über einen Index zugegriffen werden.

In [12]:
dataF.select(expr('hits[1]')).show(3,truncate=False)
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|hits[1]                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|[[www.googlemerchandisestore.com/home, www.googlemerchandisestore.com/home, 0, www.googlemerchandisestore.com/home], [(not set), (not set), (not set), (not set), (not set),,,, (not set), (not set), (not set), (not set), (not set)], [], [], [], [0,, 1],, [true], [], 2, 1,,, true,,, 34, [www.googlemerchandisestore.com, /home, /home, , , , Google Online Store,,], [], [],, [], https://www.google.com.sg/, [No,  : , (not set)], 5628,, PAGE]                                                                                                                                                                                                                                                                   |
|[[shop.googlemerchandisestore.com/google+redesign/electronics, www.googlemerchandisestore.com/home, 0, www.googlemerchandisestore.com/home], [(not set), (not set), (not set), (not set), (not set),,,, (not set), (not set), (not set), (not set), (not set)], [], [], [], [0,, 1],, [true], [], 2, 4,,, true,,, 44, [www.googlemerchandisestore.com, /home, /home, , , , Google Online Store,,], [], [],, [], https://www.google.it/, [No,  : , (not set)], 23406,, PAGE]                                                                                                                                                                                                                                              |
|[[shop.googlemerchandisestore.com/google+redesign/electronics, www.googlemerchandisestore.com/home, 0, www.googlemerchandisestore.com/home], [(not set), (not set), (not set), (not set), (not set),,,, (not set), (not set), (not set), (not set), (not set)], [], [], [], [0,, 1],, [true], [], 2, 9,,, true,,, 31, [www.googlemerchandisestore.com, /home, /home, , , , Google Online Store,,], [], [],, [], https://www.google.co.uk/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&sqi=2&ved=0ahUKEwjJuKfthOzOAhXKL8AKHZpHBhMQFggcMAA&url=https%3A%2F%2Fwww.googlemerchandisestore.com%2F&usg=AFQjCNGK7lG66tWBMQkleeCMprO_xNS6qw&sig2=bI7YzaxvZNBLmNas9gNX_A&bvm=bv.131286987,d.d24, [No,  : , (not set)], 1401314,, PAGE]|
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
only showing top 3 rows

3.1 Device - eingesetztes Gerät

Anzahl der Besuche nach verschiedenen Merkmalen

In [13]:
dataF.groupBy("device.browser").count().orderBy(desc("count")).show()
+--------------------+------+
|             browser| count|
+--------------------+------+
|              Chrome|289040|
|              Safari|106647|
|             Firefox| 16364|
|   Internet Explorer|  8281|
|                Edge|  4212|
|               Opera|  3267|
|          Opera Mini|  2403|
|     Safari (in-app)|  2070|
|           YaBrowser|  1061|
|          UC Browser|   780|
|     Android Webview|   731|
|             Coc Coc|   327|
|     Android Browser|   261|
|         Amazon Silk|   240|
|            MRCHROME|   169|
|Mozilla Compatibl...|   133|
|             Maxthon|   120|
|          BlackBerry|    94|
|    Nintendo Browser|    70|
|       Nokia Browser|    31|
+--------------------+------+
only showing top 20 rows

In [14]:
dataF.agg(countDistinct("device.browser")).show()
+------------------------------+
|count(DISTINCT device.browser)|
+------------------------------+
|                            42|
+------------------------------+

... es gibt 42 browser...

In [15]:
dataF.groupBy("device.deviceCategory").count().orderBy(desc("count")).show()
+--------------+------+
|deviceCategory| count|
+--------------+------+
|       desktop|349772|
|        mobile| 74693|
|        tablet| 11928|
+--------------+------+

In [16]:
dataF.groupBy("device.operatingSystem").count().orderBy(desc("count")).show()
+---------------+------+
|operatingSystem| count|
+---------------+------+
|        Windows|177977|
|      Macintosh|145949|
|        Android| 45171|
|            iOS| 38154|
|          Linux| 15557|
|      Chrome OS| 10756|
|      (not set)|  1880|
|  Windows Phone|   590|
|     BlackBerry|   106|
|        Samsung|    92|
|   Nintendo Wii|    67|
|     Firefox OS|    57|
|           Xbox|    32|
|        FreeBSD|     2|
|          Nokia|     2|
|          SunOS|     1|
+---------------+------+

Durchschnitt-Revenue nach verwendeten Browser

Die Spalten eines DataFrame werden als String eingelesen. Um das Durschnittsrevenue zu berechnen bei der nächsten Queries casten wir die Spalte zu einem double und ersetzen null entsprechend

In [17]:
dataF.select("device.browser",expr("totals.transactionRevenue").cast("double").alias("revenue")).fillna(0.0).groupBy("browser").avg("revenue").orderBy(desc("avg(revenue)")).show()
+-------------------+------------------+
|            browser|      avg(revenue)|
+-------------------+------------------+
|             Chrome|2180227.5117630777|
|               Edge|  786156.220322887|
|            Firefox| 382613.6641407969|
|  Internet Explorer|303738.67890351405|
|             Safari|235921.21672433356|
|        Amazon Silk|124958.33333333333|
|    Android Webview| 64158.68673050615|
|    Safari (in-app)|21217.391304347828|
|              Opera| 17438.01652892562|
|         BlackBerry|               0.0|
|          Lunascape|               0.0|
|osee2unifiedRelease|               0.0|
|     LYF_LS_4002_12|               0.0|
|            Mozilla|               0.0|
|        ThumbSniper|               0.0|
|                ADM|               0.0|
|                 YE|               0.0|
|          YaBrowser|               0.0|
|          Konqueror|               0.0|
|  Hisense M20-M_LTE|               0.0|
+-------------------+------------------+
only showing top 20 rows

Visualisierung mit pandas und plotly

DataFrames können mit Hilfe der Funktion toPandas() zu einem Pandas-Objekt konvertiert werden, das als Input für eine der weitverbreiteten python Graphik-Bibliotheken wie matplolib oder plotly dient. Aber Vorsicht: mit Spark kann man prinzipiell sehr große Datenmengen verarbeiten, dieser Weg der Visualisierung eignet sich jedoch nur für kleinere Datenmengen, da ansonsten sehr große Pandas-Objekte erzeugt werden, die in Spark nicht parallelisiert verarbeitet werden können und einen Flaschenhals in der Berechnung darstellen.

In [6]:
import plotly.plotly as py
import plotly.graph_objs as go
import pandas as pd
import requests
from plotly.offline import iplot, init_notebook_mode
from plotly import tools
init_notebook_mode()


devCat = dataF.groupBy("device.deviceCategory").count().orderBy(desc("count")).toPandas()

iplot(go.Figure(layout=dict(title='Besuche nach Geräteart') , data= [go.Pie(labels=devCat["deviceCategory"], values=devCat["count"])]))

avgRev = dataF.select("device.browser",expr("totals.transactionRevenue").cast("double").alias("revenue")).fillna(0.0).groupBy("browser").agg(avg("revenue").alias("avg_revenue")).where(expr("avg_revenue > 0")).orderBy(desc("avg_revenue")).toPandas()

iplot(go.Figure(layout=dict(title='Durchschnittl. Einnahmen nach Browser') , data= [go.Bar( y = avgRev["avg_revenue"], x = avgRev["browser"])]))