Vorbemerkungen

Dieses Dokument beschreibt die Vorprozessierung und explorative Analyse des Datensatzes, der Grundlage des auf srf.ch veröffentlichten Artikel Diese Telekom-Anbieter sorgen für den meisten Unmut ist.

SRF Data legt Wert darauf, dass die Datenvorprozessierung und -Analyse nachvollzogen und überprüft werden kann. SRF Data glaubt an das Prinzip offener Daten, aber auch offener und nachvollziehbarer Methoden. Zum anderen soll es Dritten ermöglicht werden, auf dieser Vorarbeit aufzubauen und damit weitere Auswertungen oder Applikationen zu generieren.

Die Endprodukte des vorliegenden Scripts, neben der vorliegenden explorativen Analyse, sind (Datenbeschreibung siehe unten):

  • reasons_top_four_providers.csv
  • cases_per_type.csv
  • cases_top_4_with_exit.csv

R-Script & Daten

Die Vorprozessierung und Analyse wurde im Statistikprogramm R vorgenommen. Das zugrunde liegende Script sowie die prozessierten Daten können unter diesem Link heruntergeladen werden. Durch Ausführen von main.Rmd kann der hier beschriebene Prozess nachvollzogen und der für den Artikel verwendete Datensatz generiert werden. Dabei werden Daten aus dem Ordner input eingelesen und Ergebnisse in den Ordner output geschrieben.

SRF Data verwendet das rddj-template von Timo Grossenbacher als Grundlage für seine R-Scripts. Entstehen bei der Ausführung dieses Scripts Probleme, kann es helfen, die Anleitung von rddj-template zu studieren.

Debug-Informationen: This report was generated on 2019-11-19 14:12:02. R version: 3.5.3 on x86_64-pc-linux-gnu. For this report, CRAN packages as of 2019-03-01 were used.

GitHub

Der Code für die vorliegende Datenprozessierung ist auf https://github.com/srfdata/2019-11-ombudscom zur freien Verwendung verfügbar.

Weitere Projekte

Code & Daten von SRF Data sind unter https://srfdata.github.io verfügbar.

Haftungsausschluss

Die veröffentlichten Informationen sind sorgfältig zusammengestellt, erheben aber keinen Anspruch auf Aktualität, Vollständigkeit oder Richtigkeit. Es wird keine Haftung übernommen für Schäden, die durch die Verwendung dieses Scripts oder der daraus gezogenen Informationen entstehen. Dies gilt ebenfalls für Inhalte Dritter, die über dieses Angebot zugänglich sind.

Datenbeschreibung

cases_per_type.csv

Attribut Typ Beschreibung
year Numeric Jahr der Beobachtung
Fernmeldedienst Numeric Anzahl Fälle der Fernmeldedienstanbieter
Mehrwertdienst Numeric Anzahl Fälle der Mehrwertdienstanbieter

cases_top_4_with_exit.csv

Exportiert werden nur die drei aus unserer Sicht wichtigsten Ausgänge. Andere bezieht sich sowohl auf Fernmeldedienstanbieter wie auch auf Mehrwertdienstanbieter, auch hier sind die Jahre 2014-18 abgebildet.

Attribut Typ Beschreibung
category String Ausgang des Falls in Worten
Salt Numeric Anzahl Fälle 2014-2018 mit diesem Ausgang bei Salt
Sunrise Numeric Anzahl Fälle 2014-2018 mit diesem Ausgang bei Sunrise
Swisscom Numeric Anzahl Fälle 2014-2018 mit diesem Ausgang bei Swisscom
UPC Numeric Anzahl Fälle 2014-2018 mit diesem Ausgang bei UPC
Andere Numeric Anzahl Fälle 2014-2018 mit diesem Ausgang bei allen anderen Providern

reasons_top_four_providers.csv

Diese Zahlen beziehen sich nur auf die Fälle der 4 grössten Provider Salt, Sunrise, Swisscom und UPC, auch hier sind die Jahre 2014-18 abgebildet.

Attribut Typ Beschreibung
category String Beschwerdegrund
value Numeric Anzahl Fälle 2014-2018 mit diesem Beschwerdegrund

Originalquelle

Originalquelle der Auswertungen sind Daten, die SRF im Rahmen des Öffentlichkeitsprinzips von der Ombudscom herausverlangt hat. Die betreffenden Excel-Dateien, die SRF zugeschickt worden waren, finden sich im Ordner input/data.

Lesen Sie mehr über das Verfahren eines Ombudscom-Falls auf der betreffenden Website oder im letzten Bericht (2018). Wichtig ist uns die Unterscheidung zwischen einer Anfrage und einem Fall. Es wird wie folgt beschrieben: “Eine Anfrage wird statistisch erfasst, wenn sich eine Person schriftlich oder telefonisch an die Schlichtungsstelle wendet und die Voraussetzungen für eine Einleitung des Schlichtungsverfahrens gemäss Art. 8 Prozess- und Gebührenreglement noch nicht erfüllt sind.”

Das bedeutet, dass sich diese Analyse hauptsächlich auf die Fälle und nicht auf die Anfragen konzentriert. So wurden beispielsweise im Jahr 2018 die folgenden Untergruppen von Anfragen protokolliert:

  • 564 abgeschlossen, weil sie zu einem Fall wurden
  • 872 Abschluss infolge nicht eingereichter Dokumente
  • 238 Rückzug der Anfrage
  • 101 Unzuständigkeit
  • 1344 telefonische Anfragen betreffend Informationen/Ratschläge
  • 184 schriftliche Anfragen betreffend Informationen/Ratschläge

Wie im Jahresbericht 2018 auf Seite 9 angegeben. Im Erklärungstext ist zu verstehen, dass die Anfragen allein vielleicht keine sehr zuverlässige Informationsquelle sind: “872 abgeschlossene Anfragen sind auf nicht eingereichte Dokumente zurückzuführen. Diese machten knapp 26% aller Anfragen aus. Gegenüber dem Vorjahr stieg dieser Wert leicht. Oft sind den Kundinnen und Kunden die Hürden zur Durchführung eines Schlichtungsverfahrens zu hoch. Bereits das Ausfüllen eines speziell für das Schlichtungsverfahren vorgesehenen Formulars bereitete einigen Personen Mühe. Oftmals gingen sie fälschlicherweise auch davon aus, dass der Ombudsmann ihre Interessen gegenüber den Anbietern vertritt oder die streitige Angelegenheit umgehend (z.B. mit einem Telefonat mit dem betroffenen Anbieter) lösen kann. Auch wurden sie von auf die Schlichtungsstelle verweisenden Stellen falsch über die Funktion und die Aufgaben der Schlichtungsstelle informiert. Die Mitarbeitenden der Schlichtungsstelle klärten die Kundinnen und Kunden über die Tätigkeit auf und informierten sie über die Eintretensvoraussetzungen: Bevor ein Schlichtungsverfahren eingeleitet werden kann, muss die begehrende Partei das Formular „Schlichtungsbegehren“ unter Angabe des Sachverhalts und Ziels ausfüllen sowie glaubhaft darlegen, dass sie sich innerhalb der letzten 12 Monate erfolglos um eine Lösung des Problems bemüht hatte. Ging aus der Anfrage nicht hervor, dass die Kundin oder der Kunde bereits eine Lösung mit dem Anbieter angestrebt hatte, empfahl die Schlichtungsstelle, sich schriftlich mit einer Beschwerde an den betroffenen Anbieter zu wenden. Dieser Aufwand war vielen Kundinnen und Kunden zu gross. Sie gingen – wie bereits erwähnt – davon aus, dass die Schlichtungsstelle ihre Interessen vertreten könne und sie in der Angelegenheit nichts weiter unternehmen müssen. Daher strebten sie kein Schlichtungsverfahren an und die Anfragen wurden infolge nicht eingereichter Dokumente abgeschlossen.”

Vorbereitungen

## [1] "package package:rmarkdown detached"
## Loading required package: knitr
## Loading required package: rstudioapi

Packages definieren

# from https://mran.revolutionanalytics.com/web/packages/checkpoint/vignettes/using-checkpoint-with-knitr.html
# if you don't need a package, remove it from here (commenting is probably not sufficient)
# tidyverse: see https://blog.rstudio.org/2016/09/15/tidyverse-1-0-0/
cat("
library(rstudioapi)
library(tidyverse) # ggplot2, dplyr, tidyr, readr, purrr, tibble
library(glue) # string literals
library(magrittr) # pipes
library(readxl) # excel
library(scales) # scales for ggplot2
library(jsonlite) # json
library(lintr) # code linting
library(ggrepel) # repelling geom_text
library(rmarkdown)",
file = "manifest.R")

Packages installieren

# if checkpoint is not yet installed, install it (for people using this
# system for the first time)
if (!require(checkpoint)) {
  if (!require(devtools)) {
    install.packages("devtools", repos = "http://cran.us.r-project.org")
    require(devtools)
  }
  devtools::install_github("RevolutionAnalytics/checkpoint",
                           ref = "v0.3.2", # could be adapted later,
                           # as of now (beginning of July 2017
                           # this is the current release on CRAN)
                           repos = "http://cran.us.r-project.org")
  require(checkpoint)
}
# nolint start
if (!dir.exists("~/.checkpoint")) {
  dir.create("~/.checkpoint")
}
# nolint end
# install packages for the specified CRAN snapshot date
checkpoint(snapshotDate = package_date,
           project = path_to_wd,
           verbose = T,
           scanForPackages = F,
           use.knitr = F,
           R.version = R_version)
rm(package_date)

Packages laden

source("manifest.R")
unlink("manifest.R")
sessionInfo()
## R version 3.5.3 (2019-03-11)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 18.04.3 LTS
## 
## Matrix products: default
## BLAS: /opt/R/R-3.5.3/lib/R/lib/libRblas.so
## LAPACK: /opt/R/R-3.5.3/lib/R/lib/libRlapack.so
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=de_CH.UTF-8        LC_COLLATE=en_US.UTF-8    
##  [5] LC_MONETARY=de_CH.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=de_CH.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=de_CH.UTF-8 LC_IDENTIFICATION=C       
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] rmarkdown_1.11   ggrepel_0.8.0    lintr_1.0.3      jsonlite_1.6    
##  [5] scales_1.0.0     readxl_1.3.0     magrittr_1.5     glue_1.3.0      
##  [9] forcats_0.4.0    stringr_1.4.0    dplyr_0.8.0.1    purrr_0.3.0     
## [13] readr_1.3.1      tidyr_0.8.2      tibble_2.0.1     ggplot2_3.1.0   
## [17] tidyverse_1.2.1  checkpoint_0.4.0 rstudioapi_0.9.0 knitr_1.21      
## 
## loaded via a namespace (and not attached):
##  [1] Rcpp_1.0.0       cellranger_1.1.0 plyr_1.8.4       pillar_1.3.1    
##  [5] compiler_3.5.3   tools_3.5.3      digest_0.6.18    lubridate_1.7.4 
##  [9] evaluate_0.13    nlme_3.1-137     gtable_0.2.0     lattice_0.20-38 
## [13] pkgconfig_2.0.2  rlang_0.3.1      rex_1.1.2        cli_1.0.1       
## [17] yaml_2.2.0       haven_2.1.0      xfun_0.5         withr_2.1.2     
## [21] xml2_1.2.0       httr_1.4.0       hms_0.4.2        generics_0.0.2  
## [25] grid_3.5.3       tidyselect_0.2.5 R6_2.4.0         modelr_0.1.4    
## [29] backports_1.1.3  htmltools_0.3.6  rvest_0.3.2      assertthat_0.2.0
## [33] colorspace_1.4-0 stringi_1.3.1    lazyeval_0.2.1   munsell_0.5.0   
## [37] broom_0.5.1      crayon_1.3.4
rm(list = ls(all = TRUE))

Daten einlesen

# prepare multiple tibbles that will be filled with data
status <- tibble() # Statistik
reasons_cases_requests <- tibble() # Beschwerdegründe (Fälle und Anfragen)
reasons_cases <- tibble() # Beschwerdegründe (nur Fälle)
reasons_requests <- tibble() # Beschwerdegründe (nur Anfragen)
languages <- tibble() # Sprache Beschwerdeführer
appellant_types <- tibble() # Art Beschwerdeführer
avg_sums <- tibble() # Durchschnittliche Streitsummen
avg_durations <- tibble() # Durchschnittlichen Behandlungsdauer

# the files have a consistent structure
c("FDA", "MWDA") %>%
  walk(function(current_type) {
    list.files(
      "input/data",
      pattern = glue("^\\d{{4}}_{current_type}.xlsx$"),
      full.names = TRUE
    ) %>%
      walk(function(current_file) {
        # create helper function that gathers providers
        # renames first column to category and appends info about year/file
        add_info_and_gather <- function(df) {
          df %>%
            rename(category = 1) %>%
            mutate(
              year = as.numeric(str_extract(current_file, "\\d{4}")),
              type = str_replace_all(current_type, c(
                "FDA" = "Fernmeldedienst",
                "MWDA" = "Mehrwertdienst"
              ))
            ) %>%
            gather(
              key = "provider",
              value = "value",
              -one_of("category", "year", "type", "file")
            ) %>%
            # convert to numeric, but first remove dots (thousands) and
            # replace commas (decimal) with point
            mutate(
              value = str_replace_all(value, "\\.", ""),
              value = str_replace_all(value, ",", "."),
              value = as.numeric(value)
            )
        }

        # read ranges and bind to prepared tibbles
        suppressMessages({
          status <<- status %>%
            bind_rows(
              read_excel(current_file, skip = 1, n_max = 37) %>%
                add_info_and_gather()
            )

          reasons_cases_requests <<- reasons_cases_requests %>%
            bind_rows(
              read_excel(current_file, skip = 40, n_max = 22) %>%
                add_info_and_gather()
            )

          reasons_cases <<- reasons_cases %>%
            bind_rows(
              read_excel(current_file, skip = 65, n_max = 22) %>%
                add_info_and_gather()
            )

          reasons_requests <<- reasons_requests %>%
            bind_rows(
              read_excel(current_file, skip = 90, n_max = 22) %>%
                add_info_and_gather()
            )

          languages <<- languages %>%
            bind_rows(
              read_excel(current_file, skip = 115, n_max = 4) %>%
                add_info_and_gather()
            )

          appellant_types <<- appellant_types %>%
            bind_rows(
              read_excel(current_file, skip = 122, n_max = 3) %>%
                add_info_and_gather()
            )

          avg_sums <<- avg_sums %>%
            bind_rows(
              read_excel(current_file, skip = 128, n_max = 2) %>%
                add_info_and_gather()
            )

          avg_durations <<- avg_durations %>%
            bind_rows(
              read_excel(current_file, skip = 133, n_max = 8) %>%
                add_info_and_gather()
            )
        })
      })
  })

# In our tables we still have some totals. Not only in the column category
# where it is the sum of all other categories, but also in the column provider
# we remove all these, we can sum stuff on our own perfectly well.
# Also convert category, type, provider to factor in all tibbles, don't be
# confused by the syntax with walk / assign, it's really just a fancy way
# to apply the same mutation to all variables named in the vector.
c(
  "status",
  "reasons_cases_requests",
  "reasons_cases",
  "reasons_requests",
  "languages",
  "appellant_types",
  "avg_sums",
  "avg_durations"
) %>%
  walk(function(current_tibble) {
    # create new variables with names …_totals
    assign(
      glue("{current_tibble}_totals"),
      get(current_tibble) %>%
        # now remove totals from original tables
        filter(
          str_detect(category, "^Total") |
          str_detect(provider, "^Total")
        ),
      envir = .GlobalEnv
    )
    # apply mutation to all variables
    assign(
      current_tibble,
      get(current_tibble) %>%
        # now remove totals from original tables
        filter(
          !str_detect(category, "^Total") &
          !str_detect(provider, "^Total")
        ) %>%
        mutate_at(
          vars(category, type, provider),
          factor
        ),
      envir = .GlobalEnv
    )
  })

Auswertungen

Gesamtanzahl Fälle und Anfragen

Die Tabelle status enthält zwei Gruppen: Fälle und Anfragen abgeschlossen. In der Spalte category sind auch die Unterkategorien aufgeführt, aber diese beiden sind geeignet, um sich die Summen pro Jahr anzusehen, z.B:

top_four <- c("Salt Mobile SA", "Swisscom (Schweiz) AG",
              "Sunrise Communications AG", "UPC Schweiz GmbH")
status %>%
  filter(category == "Fälle" | category == "Anfragen abgeschlossen") %>%
  group_by(category, year) %>%
  summarise(sum = sum(value)) %>%
  spread(key = category, value = sum) %>%
  mutate(Total = `Anfragen abgeschlossen` + `Fälle`) %>%
  knitr::kable()
year Anfragen abgeschlossen Fälle Total
2014 5036 1184 6220
2015 4849 1420 6269
2016 5115 1333 6448
2017 4183 1125 5308
2018 3330 1078 4408
status %>%
  filter(category == "Fälle" | category == "Anfragen abgeschlossen") %>%
  group_by(category, year, type) %>%
  summarise(sum = sum(value)) %>%
  ggplot(
    aes(
      x = year,
      y = sum,
      fill = type
    )
  ) +
  geom_bar(stat = "identity") +
  scale_fill_brewer(palette = "Set1", guide = FALSE) +
  facet_grid(category ~ type, scales = "fixed") +
  theme_minimal() +
  labs(
    title = "Anzahl Fälle und Anfragen pro Jahr",
    x = NULL,
    y = NULL,
    fill = NULL
  )

# csv for graphic in article
write_csv(
  status %>%
    filter(category == "Fälle") %>%
    select(-category) %>%
    group_by(year, type) %>%
    summarise(sum = sum(value)) %>%
    spread(type, sum),
  path = "output/cases_per_type.csv"
)

Anzahl Provider

Die Anzahl der Anbieter / Provider scheint über Jahre hinweg einheitlich zu sein.

status %>%
  distinct(type, provider, year) %>%
  group_by(type, year) %>%
  count() %>%
  knitr::kable()
type year n
Fernmeldedienst 2014 188
Fernmeldedienst 2015 188
Fernmeldedienst 2016 188
Fernmeldedienst 2017 188
Fernmeldedienst 2018 188
Mehrwertdienst 2014 203
Mehrwertdienst 2015 203
Mehrwertdienst 2016 203
Mehrwertdienst 2017 203
Mehrwertdienst 2018 203

Höchste Anzahl Fälle

Nun identifizieren wir die “grossen Fische” über alle fünf Jahre hinweg, sortiert nach der Gesamtzahl der Fälle:

top_20 <- status %>%
  filter(category == "Fälle") %>%
  group_by(provider, type, category) %>%
  summarise(sum = sum(value)) %>%
  ungroup() %>%
  spread(key = category, value = sum) %>%
  arrange(desc(`Fälle`)) %>%
  top_n(20)

top_20 %>%
  knitr::kable()
provider type Fälle
Salt Mobile SA Fernmeldedienst 1354
Sunrise Communications AG Fernmeldedienst 734
primacall AG Fernmeldedienst 680
UPC Schweiz GmbH Fernmeldedienst 654
Suissephone Communications GmbH Fernmeldedienst 552
Swisscom (Schweiz) AG Fernmeldedienst 520
FREEFON AG Fernmeldedienst 222
Echovox SA Mehrwertdienst 180
Kira Consulting & Solutions AG Mehrwertdienst 156
NTH AG Mehrwertdienst 140
TalkEasy GmbH Fernmeldedienst 92
Richberg Media AG Mehrwertdienst 74
TalkTalk AG Fernmeldedienst 66
Dimoco Europe GmbH Mehrwertdienst 40
Buongiorno Schweiz AG Mehrwertdienst 39
OneCom GmbH Fernmeldedienst 39
Terrifix AG Mehrwertdienst 38
MOBIYO SAS Mehrwertdienst 29
MobileTrade AG Mehrwertdienst 26
TelCommunication Services AG Fernmeldedienst 23
top_20 %<>%
  pull(provider)

# define a categorical color scale with looots of values
categorical <- c(
  "#e31f2b", "#f7a600", "#a8b51c",
  "#61b13e", "#1cb373", "#1cb0b5",
  "#1e8ce3", "#a359c0", "#ca51af",
  "#9f9c90" # grey
)

Fälle nach Provider und Jahr

top_10 <- top_20[1:10]

status %>%
  filter(
    provider %in% top_10 &
    category == "Fälle"
  ) %>%
  ggplot(
    aes(
      x = year,
      y = value,
      group = provider,
      color = provider
    )
  ) +
  geom_line() +
  geom_text_repel(
    data = status %>%
      filter(
        provider %in% top_10 &
        year == max(year) &
        category == "Fälle"
      ),
    mapping = aes(
      label = provider
    ),
    segment.colour = "#DDDDDD",
    direction = "y",
    nudge_x = 0.5,
    hjust = 0
  ) +
  scale_color_manual(values = categorical) +
  scale_x_continuous(
    breaks = seq(min(status$year), max(status$year)),
    limits = c(NA, max(avg_sums$year) + 3)
  ) +
  theme_minimal() +
  theme(legend.position = "none") +
  labs(
    title = "Anzahl Fälle der 10 Provider mit den meisten Fällen",
    x = NULL,
    y = NULL
  )

Durchschn. Streitsummen

Unter den Top-10-Anbietern mit den meisten Fällen analysieren wir auch die durchschnittliche Streitsumme pro Fall und Jahr:

avg_sums %>%
  filter(
    provider %in% top_10 &
    category == "Streitsumme CHF"
  ) %>%
  ggplot(
    aes(
      x = year,
      y = value,
      group = provider,
      color = provider
    )
  ) +
  geom_line() +
  geom_text_repel(
    data = avg_sums %>%
      filter(
        provider %in% top_10 &
        category == "Streitsumme CHF" &
        year == max(year)
      ),
    mapping = aes(
      label = provider
    ),
    segment.colour = "#DDDDDD",
    direction = "y",
    nudge_x = 0.5,
    hjust = 0
  ) +
  scale_y_log10(labels = dollar_format(prefix = "CHF")) +
  scale_x_continuous(
    breaks = seq(min(status$year), max(status$year)),
    limits = c(NA, max(avg_sums$year) + 3)
  ) +
  scale_color_manual(values = categorical) +
  theme_minimal() +
  theme(legend.position = "none") +
  labs(
    title =
      "Durchschnittliche Streitsumme der 10 Provider mit den meisten Fällen",
    subtitle = "Hinweis: Die y-Achse ist logarithmisch",
    x = NULL,
    y = NULL
  )

Die Durchschnittssumme pro Fall der Top-10-Anbieter liegt in der Regel zwischen 100 und 500 Schweizer Franken, mit Ausnahme der UPC Schweiz GmbH im Jahr 2014, wo sie CHF 2’397 betrug.

Ausgang Fälle

Aufschlüsselung nach Rückzug, Schlichtung und Nicht-Schlichtung über alle Jahre und Top-10-Anbieter:

relevant_status <- c(
  "Schlichtung zustande gekommen",
  "Rückzug Kunde nach Einleitung",
  "Schlichtung nicht zustande gekommen"
)

status %>%
  filter(
    category %in% relevant_status &
    provider %in% top_10
  ) %>%
  mutate(provider = factor(provider, levels = top_10)) %>%
  ggplot(aes(
    x = year,
    y = value,
    fill = category
  )) +
  geom_bar(stat = "identity") +
  facet_wrap(~ provider) +
  scale_fill_manual(values = c("#9d9d9d", "#e31f2b", "#61b13e")) +
  theme_minimal() +
  labs(
    title = "Fälle und deren Ausgang nach Provider, Jahr und Kategorie",
    subtitle = "Top 10 der Provider mit den meisten Fällen 2014-2018",
    x = NULL,
    y = NULL,
    fill = NULL
  )

# table for "top four"
status %>%
  filter(
    category %in% relevant_status &
    provider %in% top_four
  ) %>%
  group_by(provider, year) %>%
  summarize(sum = sum(value)) %>%
  knitr::kable()
provider year sum
Salt Mobile SA 2014 239
Salt Mobile SA 2015 347
Salt Mobile SA 2016 320
Salt Mobile SA 2017 210
Salt Mobile SA 2018 238
Sunrise Communications AG 2014 164
Sunrise Communications AG 2015 151
Sunrise Communications AG 2016 127
Sunrise Communications AG 2017 147
Sunrise Communications AG 2018 145
Swisscom (Schweiz) AG 2014 53
Swisscom (Schweiz) AG 2015 72
Swisscom (Schweiz) AG 2016 158
Swisscom (Schweiz) AG 2017 145
Swisscom (Schweiz) AG 2018 92
UPC Schweiz GmbH 2014 63
UPC Schweiz GmbH 2015 107
UPC Schweiz GmbH 2016 131
UPC Schweiz GmbH 2017 191
UPC Schweiz GmbH 2018 162
# csv for graphic in article
write_csv(
  status %>%
    filter(
      category %in% relevant_status
    ) %>%
    mutate(provider = if_else(
      provider %in% top_four,
      as.character(provider),
      "Andere"
    )) %>%
    group_by(provider, category) %>%
    summarise(value = sum(value)) %>%
    spread(provider, value) %>%
    select(
      category,
      `Salt` = `Salt Mobile SA`,
      `Sunrise` = `Sunrise Communications AG`,
      `Swisscom` = `Swisscom (Schweiz) AG`,
      `UPC` = `UPC Schweiz GmbH`,
      Andere
    ) %>%
    mutate(category = factor(category, levels = c(
      "Schlichtung nicht zustande gekommen",
      "Schlichtung zustande gekommen",
      "Rückzug Kunde nach Einleitung"
    ))) %>%
    arrange(category),
  path = "output/cases_top_4_with_exit.csv"
)

Genauere Aufschlüsselung

Nachfolgend werden die Daten aller Jahre zusammengefasst, die Rückzüge entfernt und die Einigungen / Ablehnungen genauer aufgeschlüsselt.

relevant_status <- c(
  "Einigung zugunsten Kunde",
  "Einigung zugunsten Provider",
  "Einigung Kunde und Provider",
  "Ablehnung Kunde",
  "Ablehnung Provider",
  "Ablehnung Kunde und Provider",
  "Unzuständigkeit nach Einleitung",
  "Rückzug Kunde nach Einleitung"
)
status_aggregated <- status %>%
  filter(
    category %in% relevant_status &
    provider %in% top_10
  ) %>%
  mutate(provider = factor(provider, levels = top_10)) %>%
  mutate(category = factor(category, levels = relevant_status)) %>%
  group_by(provider, category) %>%
  summarize(total_category = sum(value)) %>%
  ungroup()

status_aggregated %>%
  ggplot(aes(
    x = provider,
    y = total_category,
    fill = category
  )) +
  geom_bar(stat = "identity") +
  scale_fill_manual(values = c("#579d39", "#72bd51", "#b3df9f",
                               "#c91024", "#f1434a", "#ff9193", "#ffe6e6",
                               "#9d9d9d")) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, vjust = 1.2)) +
  labs(
    title = "Fälle und deren Ausgang nach Provider, 2014-2018",
    subtitle = "Top 10 der Provider mit den meisten Fällen 2014-2018",
    x = NULL,
    y = NULL,
    fill = NULL
  )

# table for "top four"
status_aggregated %>%
  filter(provider %in% top_four) %>%
  knitr::kable()
provider category total_category
Salt Mobile SA Einigung zugunsten Kunde 118
Salt Mobile SA Einigung zugunsten Provider 10
Salt Mobile SA Einigung Kunde und Provider 355
Salt Mobile SA Ablehnung Kunde 81
Salt Mobile SA Ablehnung Provider 129
Salt Mobile SA Ablehnung Kunde und Provider 38
Salt Mobile SA Unzuständigkeit nach Einleitung 3
Salt Mobile SA Rückzug Kunde nach Einleitung 620
Sunrise Communications AG Einigung zugunsten Kunde 147
Sunrise Communications AG Einigung zugunsten Provider 9
Sunrise Communications AG Einigung Kunde und Provider 259
Sunrise Communications AG Ablehnung Kunde 56
Sunrise Communications AG Ablehnung Provider 30
Sunrise Communications AG Ablehnung Kunde und Provider 28
Sunrise Communications AG Unzuständigkeit nach Einleitung 1
Sunrise Communications AG Rückzug Kunde nach Einleitung 204
UPC Schweiz GmbH Einigung zugunsten Kunde 299
UPC Schweiz GmbH Einigung zugunsten Provider 12
UPC Schweiz GmbH Einigung Kunde und Provider 215
UPC Schweiz GmbH Ablehnung Kunde 45
UPC Schweiz GmbH Ablehnung Provider 14
UPC Schweiz GmbH Ablehnung Kunde und Provider 4
UPC Schweiz GmbH Unzuständigkeit nach Einleitung 2
UPC Schweiz GmbH Rückzug Kunde nach Einleitung 63
Swisscom (Schweiz) AG Einigung zugunsten Kunde 61
Swisscom (Schweiz) AG Einigung zugunsten Provider 8
Swisscom (Schweiz) AG Einigung Kunde und Provider 97
Swisscom (Schweiz) AG Ablehnung Kunde 56
Swisscom (Schweiz) AG Ablehnung Provider 29
Swisscom (Schweiz) AG Ablehnung Kunde und Provider 16
Swisscom (Schweiz) AG Unzuständigkeit nach Einleitung 1
Swisscom (Schweiz) AG Rückzug Kunde nach Einleitung 252
Rückzugsquote
status_aggregated %>%
  group_by(provider) %>%
  mutate(share = round(total_category / sum(total_category), 2)) %>%
  filter(str_detect(category, "Rückzug")) %>%
  select(provider, share) %>%
  arrange(desc(share)) %>%
  knitr::kable()
provider share
NTH AG 0.69
FREEFON AG 0.67
primacall AG 0.66
Suissephone Communications GmbH 0.64
Swisscom (Schweiz) AG 0.48
Salt Mobile SA 0.46
Echovox SA 0.44
Sunrise Communications AG 0.28
Kira Consulting & Solutions AG 0.17
UPC Schweiz GmbH 0.10
Grafik ohne Rückzüge
relevant_status <- c(
  "Einigung zugunsten Kunde",
  "Einigung zugunsten Provider",
  "Einigung Kunde und Provider",
  "Ablehnung Kunde",
  "Ablehnung Provider",
  "Ablehnung Kunde und Provider",
  "Unzuständigkeit nach Einleitung"
)
status_aggregated <- status %>%
  filter(
    category %in% relevant_status &
    provider %in% top_10
  ) %>%
  mutate(provider = factor(provider, levels = top_10)) %>%
  mutate(category = factor(category, levels = relevant_status)) %>%
  group_by(provider, category) %>%
  summarize(total_category = sum(value)) %>%
  ungroup()

status_aggregated %>%
  ggplot(aes(
    x = provider,
    y = total_category,
    fill = category
  )) +
  geom_bar(stat = "identity") +
  scale_fill_manual(values = c("#579d39", "#72bd51", "#b3df9f",
                               "#c91024", "#f1434a", "#ff9193", "#ffe6e6",
                               "#9d9d9d")) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, vjust = 1.2)) +
  labs(
    title = "Fälle und deren Ausgang nach Provider, 2014-2018",
    subtitle = "Top 10 der Provider mit den meisten Fällen 2014-2018",
    x = NULL,
    y = NULL,
    fill = NULL
  )

Einigungsquote (“Schlichtung zustande gekommen”, ohne Rückzüge)
status_aggregated %>%
  filter(!str_detect(category, "Rückzug")) %>%
  mutate(agg = word(category, 1)) %>%
  group_by(provider, agg) %>%
  summarize(total_category = sum(total_category)) %>%
  mutate(share = round(total_category / sum(total_category), 2)) %>%
  filter(str_detect(agg, "Einigung")) %>%
  select(provider, share) %>%
  arrange(desc(share)) %>%
  knitr::kable()
provider share
NTH AG 0.93
Echovox SA 0.90
UPC Schweiz GmbH 0.89
FREEFON AG 0.88
primacall AG 0.82
Sunrise Communications AG 0.78
Salt Mobile SA 0.66
Swisscom (Schweiz) AG 0.62
Suissephone Communications GmbH 0.48
Kira Consulting & Solutions AG 0.42

Beschwerdegründe (Fälle)

# get top n reasons
top_n_reasons <- reasons_cases %>%
  group_by(category) %>%
  summarize(total_category = sum(value)) %>%
  arrange(desc(total_category)) %>%
  top_n(9) %>%
  pull(category)

reasons_cases %>%
  filter(
    provider %in% top_10
  ) %>%
  # keep top 10 categories and summarize rest
  mutate(category = case_when(
    category %in% top_n_reasons ~ as.character(category),
    TRUE ~ "Sonstige"
  )) %>%
  mutate(provider = factor(provider, levels = top_10)) %>%
  mutate(category = factor(category, levels =
                             c(as.character(top_n_reasons), "Sonstige"))) %>%
  group_by(provider, category) %>%
  summarize(total_category = sum(value)) %>%
  ungroup() %>%
  ggplot(aes(
    x = provider,
    y = total_category,
    fill = category
  )) +
  geom_bar(stat = "identity") +
  scale_fill_manual(values = categorical) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, vjust = 1.2)) +
  labs(
    title = "Beschwerdegründe (Fälle) nach Provider, 2014-2018",
    subtitle = "Top 10 der Provider mit den meisten Fällen 2014-2018",
    x = NULL,
    y = NULL,
    fill = NULL
  )

# table for "top four"
reasons_cases %>%
  filter(provider %in% top_four) %>% # keep top 10 categories and summarize rest
  mutate(category = case_when(
    category %in% top_n_reasons ~ as.character(category),
    TRUE ~ "Sonstige"
  )) %>%
  group_by(provider, category) %>%
  summarize(sum = sum(value)) %>%
  ungroup() %>%
  group_by(provider) %>%
  arrange(provider, desc(sum)) %>%
  knitr::kable()
provider category sum
Salt Mobile SA Rechnungsstellung allg. 564
Salt Mobile SA Sperrung/Annulation Dienstleistung, Vertragskündigung 375
Salt Mobile SA Roaming 177
Salt Mobile SA Sonstige 123
Salt Mobile SA Kundendienst 54
Salt Mobile SA Unverlangte Dienstleistung 25
Salt Mobile SA MWD allg. 21
Salt Mobile SA Versorgungsqualität Festnetz 11
Salt Mobile SA MWD Erwachsenenunterhaltung 2
Salt Mobile SA Preselection 2
Sunrise Communications AG Rechnungsstellung allg. 278
Sunrise Communications AG Sperrung/Annulation Dienstleistung, Vertragskündigung 193
Sunrise Communications AG Roaming 95
Sunrise Communications AG Sonstige 75
Sunrise Communications AG Versorgungsqualität Festnetz 31
Sunrise Communications AG Kundendienst 24
Sunrise Communications AG Unverlangte Dienstleistung 18
Sunrise Communications AG MWD allg. 12
Sunrise Communications AG Preselection 4
Sunrise Communications AG MWD Erwachsenenunterhaltung 1
Swisscom (Schweiz) AG Rechnungsstellung allg. 165
Swisscom (Schweiz) AG Sperrung/Annulation Dienstleistung, Vertragskündigung 98
Swisscom (Schweiz) AG Sonstige 92
Swisscom (Schweiz) AG Versorgungsqualität Festnetz 54
Swisscom (Schweiz) AG MWD allg. 40
Swisscom (Schweiz) AG Kundendienst 24
Swisscom (Schweiz) AG Roaming 24
Swisscom (Schweiz) AG Unverlangte Dienstleistung 14
Swisscom (Schweiz) AG MWD Erwachsenenunterhaltung 3
Swisscom (Schweiz) AG Preselection 0
UPC Schweiz GmbH Rechnungsstellung allg. 305
UPC Schweiz GmbH Sperrung/Annulation Dienstleistung, Vertragskündigung 204
UPC Schweiz GmbH Unverlangte Dienstleistung 43
UPC Schweiz GmbH Sonstige 34
UPC Schweiz GmbH Versorgungsqualität Festnetz 32
UPC Schweiz GmbH Kundendienst 21
UPC Schweiz GmbH Roaming 8
UPC Schweiz GmbH MWD allg. 1
UPC Schweiz GmbH MWD Erwachsenenunterhaltung 0
UPC Schweiz GmbH Preselection 0
# csv for graphic in article
write_csv(
  reasons_cases %>%
    filter(provider %in% top_four) %>%
    group_by(category) %>%
    summarise(value = sum(value)) %>%
    arrange(desc(value)),
  path = "output/reasons_top_four_providers.csv"
)

Tabelle Beschwerdegründe

reasons_cases %>%
  # keep top 10 categories and summarize rest
  mutate(category = case_when(
    category %in% top_n_reasons ~ as.character(category),
    TRUE ~ "Sonstige"
  )) %>%
  mutate(category = factor(category, levels =
                             c(as.character(top_n_reasons), "Sonstige"))) %>%
  group_by(category) %>%
  summarize(total = sum(value)) %>%
  knitr::kable()
category total
Rechnungsstellung allg. 1431
Preselection 1227
Sperrung/Annulation Dienstleistung, Vertragskündigung 1131
MWD allg. 788
Roaming 311
Unverlangte Dienstleistung 269
MWD Erwachsenenunterhaltung 197
Versorgungsqualität Festnetz 182
Kundendienst 133
Sonstige 428

Linting

Der Code in diesem RMarkdown wird mit lintr automatisch auf den Wickham’schen tidyverse style guide überprüft.

lintr::lint("main.Rmd", linters =
  lintr::with_defaults(
    object_length_linter = object_length_linter(45)
  )
)
## main.Rmd:667:15: style: Variable and function names should be all lowercase.
##       `UPC` = `UPC Schweiz GmbH`,
##               ^~~~~~~~~~~~~~~~~~