Praxisbeispiel - Telko Vertragskundenabgang-Vorhersage - Daten simulieren/kreieren¶
Da der Original-Datensatz von kaggle schon recht weit vorbereitet ist als Feature-Matrix, möchten wir unseren eigenen dazu passenden Datensatz erstellen, um zu sehen, wie Feature Engineering auf dynamischen Datensätzen aussieht.
Hierfür nehmen wir uns das Beispiel des Kunden-Services.
Wiefolgt sind die Schritte für die Kreierung eines solchen simulierten Datensatzes:
Original-Datensatz laden
Stichtag und Vertragsbeginn definieren: Setze den Stichtag auf den 31.12.2024. Für jeden Kunden berechnen wir einen simulierten Vertragsstart, indem wir den Stichtag um die Anzahl der Monate (aus der Spalte tenure) zurückrechnen.
Anzahl der Tickets pro Kunde simulieren: Wir simulieren bzw bestimmen für jeden Kunden zufällig (z. B. basierend auf einer Poisson-Verteilung oder einer zufälligen Zahl), wie viele Tickets (also Interaktions-Threads) im Betrachtungszeitraum generiert werden.
Ticket-Interaktionen erzeugen: Für jedes Ticket erstellen wir mehrere Interaktionen (z. B. eine erste Anfrage und dann ein bis zwei Follow-ups). Dabei werden folgende Spalten befüllt:
ticket_id: Eindeutige Kennung (zum Beispiel als Kombination aus Customer-ID und einem Zähler).
customer_id: Übernimmt den Wert aus dem Telco-Datensatz.
time_request: Zufälliger Zeitpunkt der Anfrage zwischen Vertragsstart und Stichtag.
time_reply: Ein Zeitpunkt, der einige Stunden oder Tage nach time_request liegt.
channel: Zufällig ausgewählter Kommunikationskanal (z. B. “Email”, “Hotline”, “On-site”, “Chat”, “Social Media”).
request und reply: Simulierte Texte (z. B. “Problem mit Rechnung” bzw. “Problem gelöst”).
solved: Boolean, der anzeigt, ob das Problem mit der Antwort gelöst wurde (z. B. basierend auf Stichwörtern im Antworttext).
original_request: True, wenn es sich um die erste Interaktion eines Tickets handelt, sonst False.
original_ticket_id: Bei der ersten Interaktion bleibt dieser Wert None, ansonsten verweist er auf die ursprüngliche Ticket-ID.
Zusätzliche Spalten (optional: Optional können wir weitere Informationen wie beispielsweise den bearbeitenden Agenten, Ticket-Priorität oder Notizen hinzufügen.
Datensatz¶
Original Datensatz:
https://www.kaggle.com/datasets/blastchar/telco-customer-churn
[1]:
import sys
# !{sys.executable} -m pip install kagglehub
# !{sys.executable} -m pip install plotly
# !{sys.executable} -m pip install missingno
[2]:
import kagglehub
[3]:
import pandas as pd
import numpy as np
import random
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
pd.set_option('display.max_columns', None)
[4]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import warnings
[5]:
from sklearn.preprocessing import StandardScaler, LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
from sklearn.neural_network import MLPClassifier
from sklearn.ensemble import AdaBoostClassifier
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.ensemble import ExtraTreesClassifier
from sklearn.linear_model import LogisticRegression
from sklearn import metrics
from sklearn.metrics import roc_curve
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, confusion_matrix, classification_report
# from xgboost import XGBClassifier
# from catboost import CatBoostClassifier
[6]:
# Download data from Kaggle
path = kagglehub.dataset_download("blastchar/telco-customer-churn")
print("Path to files:", path)
Path to files: /Users/veit/.cache/kagglehub/datasets/blastchar/telco-customer-churn/versions/1
[7]:
path_file = "~/.cache/kagglehub/datasets/blastchar/telco-customer-churn/versions/1/WA_Fn-UseC_-Telco-Customer-Churn.csv"
print(path_file)
~/.cache/kagglehub/datasets/blastchar/telco-customer-churn/versions/1/WA_Fn-UseC_-Telco-Customer-Churn.csv
Datensatz¶
Lade den Original-Datensatz
[8]:
df = pd.read_csv(path_file)
df
[8]:
| customerID | gender | SeniorCitizen | Partner | Dependents | tenure | PhoneService | MultipleLines | InternetService | OnlineSecurity | OnlineBackup | DeviceProtection | TechSupport | StreamingTV | StreamingMovies | Contract | PaperlessBilling | PaymentMethod | MonthlyCharges | TotalCharges | Churn | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 7590-VHVEG | Female | 0 | Yes | No | 1 | No | No phone service | DSL | No | Yes | No | No | No | No | Month-to-month | Yes | Electronic check | 29.85 | 29.85 | No |
| 1 | 5575-GNVDE | Male | 0 | No | No | 34 | Yes | No | DSL | Yes | No | Yes | No | No | No | One year | No | Mailed check | 56.95 | 1889.5 | No |
| 2 | 3668-QPYBK | Male | 0 | No | No | 2 | Yes | No | DSL | Yes | Yes | No | No | No | No | Month-to-month | Yes | Mailed check | 53.85 | 108.15 | Yes |
| 3 | 7795-CFOCW | Male | 0 | No | No | 45 | No | No phone service | DSL | Yes | No | Yes | Yes | No | No | One year | No | Bank transfer (automatic) | 42.30 | 1840.75 | No |
| 4 | 9237-HQITU | Female | 0 | No | No | 2 | Yes | No | Fiber optic | No | No | No | No | No | No | Month-to-month | Yes | Electronic check | 70.70 | 151.65 | Yes |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 7038 | 6840-RESVB | Male | 0 | Yes | Yes | 24 | Yes | Yes | DSL | Yes | No | Yes | Yes | Yes | Yes | One year | Yes | Mailed check | 84.80 | 1990.5 | No |
| 7039 | 2234-XADUH | Female | 0 | Yes | Yes | 72 | Yes | Yes | Fiber optic | No | Yes | Yes | No | Yes | Yes | One year | Yes | Credit card (automatic) | 103.20 | 7362.9 | No |
| 7040 | 4801-JZAZL | Female | 0 | Yes | Yes | 11 | No | No phone service | DSL | Yes | No | No | No | No | No | Month-to-month | Yes | Electronic check | 29.60 | 346.45 | No |
| 7041 | 8361-LTMKD | Male | 1 | Yes | No | 4 | Yes | Yes | Fiber optic | No | No | No | No | No | No | Month-to-month | Yes | Mailed check | 74.40 | 306.6 | Yes |
| 7042 | 3186-AJIEK | Male | 0 | No | No | 66 | Yes | No | Fiber optic | Yes | No | Yes | Yes | Yes | Yes | Two year | Yes | Bank transfer (automatic) | 105.65 | 6844.5 | No |
7043 rows × 21 columns
Der Datensatz enthält Informationen über:
ob der Kunde innerhalb des letzten Monats gekündigt hat - Spalte „Churn“
Dienste, für die sich jeder Kunde angemeldet hat - „PhoneService“, „MultipleLines“, „InternetService“, „OnlineSecurity“, „OnlineBackup“, „DeviceProtection“, „TechSupport“ sowie „StreamingTV“ und „StreamingMovies“.
Informationen zum Kundenkonto - „tenure“ wie lange der Kunde bereits Kunde ist, „Contract“ (Vertrag), „PaperlessBilling“ (papierlose Abrechnung), „PaymentMethod“ (Zahlungsmethode), „MonthlyCharges“ (monatliche Gebühren) und „TotalCharges“ (Gesamtgebühren).
Demografische Informationen über Kunden - „gender“ (Geschlecht), „SeniorCitizen“ (Alter) und ob sie „Partner“ (Partner) und „Dependents“ (Familienangehörige) haben.
[9]:
# Für Reproduzierbarkeit definieren wir den selben Seed für den Randomisierung
np.random.seed(42)
random.seed(42)
Stichtag¶
Für unsere Simulation definieren wir einen Stichtag - das ist der Tag, an dem der Datensatz gezogen wird. Das ist auch die Referenz für das Startdatum der Tenure eines Kunden, somit dürften keine Datenpunkte vor dem berechneten Start-datum oder nach diesem Stichtag kreiert werden.
Der Stichtag in diesem Beispiel-Code ist der 2024-12-31.
[10]:
# 2. Stichtag definieren
stichtag = pd.to_datetime("2024-12-31")
stichtag
[10]:
Timestamp('2024-12-31 00:00:00')
[11]:
# Berechne den Vertragsstart als: stichtag - tenure in Monaten
df["contract_start"] = df["tenure"].apply(lambda t: stichtag - relativedelta(months=int(t)))
df[["customerID", "tenure", "contract_start"]].head()
[11]:
| customerID | tenure | contract_start | |
|---|---|---|---|
| 0 | 7590-VHVEG | 1 | 2024-11-30 |
| 1 | 5575-GNVDE | 34 | 2022-02-28 |
| 2 | 3668-QPYBK | 2 | 2024-10-31 |
| 3 | 7795-CFOCW | 45 | 2021-03-31 |
| 4 | 9237-HQITU | 2 | 2024-10-31 |
Funktion zur Berechnung des Reply-Zeitpunkts¶
Hier definieren wir eine Funktion get_reply_time, die für einen gegebenen Anfrage-Zeitpunkt (request_time) den Reply-Zeitpunkt simuliert.
Die Bedingungen sind:
Ist die Anfrage am Stichtag, erfolgt die Antwort zwangsläufig am selben Tag.
Sonst wird zufällig entschieden, ob die Antwort am selben Tag oder am nächsten Tag erfolgt.
Falls der nächste Tag den Stichtag überschreiten würde, wird ebenfalls der gleiche Tag gewählt.
[12]:
def get_reply_time(request_time, stichtag):
"""
Berechnet einen Reply-Zeitpunkt, der entweder am selben Tag
oder am Tag nach der Anfrage liegt, unter Berücksichtigung des Stichtags.
"""
# Wenn die Anfrage bereits am Stichtag erfolgt, muss die Antwort am selben Tag erfolgen.
if request_time.date() >= stichtag.date():
reply_day_offset = 0
else:
reply_day_offset = random.choice([0, 1])
if reply_day_offset == 0:
# Antwort am selben Tag: Wähle eine zufällige Verzögerung, sodass die Antwort
# zwischen 1 Stunde nach der Anfrage und bis zum Tagesende erfolgt.
end_of_day = request_time.replace(hour=23, minute=59, second=59)
max_delay_seconds = int((end_of_day - request_time).total_seconds())
# Falls genügend Zeit vorhanden ist, mindestens 1 Stunde Verzögerung, sonst minimal
min_delay = 3600 if max_delay_seconds >= 3600 else 60
delay_seconds = random.randint(min_delay, max_delay_seconds) if max_delay_seconds >= min_delay else 0
reply_time = request_time + timedelta(seconds=delay_seconds)
else:
# Antwort am nächsten Tag: Stelle sicher, dass der nächste Tag den Stichtag nicht überschreitet.
next_day = request_time + timedelta(days=1)
if next_day > stichtag:
# Fallback: Antworte am selben Tag
return get_reply_time(request_time, stichtag)
# Wähle eine zufällige Uhrzeit am nächsten Tag
random_hour = random.randint(0, 23)
random_minute = random.randint(0, 59)
random_second = random.randint(0, 59)
reply_time = datetime.combine(next_day.date(), datetime.min.time()) + timedelta(hours=random_hour, minutes=random_minute, seconds=random_second)
return reply_time
# Teste die Funktion einmal:
test_request = datetime(2024, 12, 30, 15, 0, 0)
print("Anfragezeit:", test_request)
print("Antwortzeit:", get_reply_time(test_request, stichtag))
Anfragezeit: 2024-12-30 15:00:00
Antwortzeit: 2024-12-30 16:13:39
Vor-test: Simuliere Interaktionen für einen einzelnen Kunden (Beispiel anhand des ersten Kunden)¶
[13]:
# Wähle den ersten Kunden aus dem Datensatz
customer_row = df.iloc[0]
customer_id = customer_row["customerID"]
contract_start = customer_row["contract_start"]
customer_row
[13]:
customerID 7590-VHVEG
gender Female
SeniorCitizen 0
Partner Yes
Dependents No
tenure 1
PhoneService No
MultipleLines No phone service
InternetService DSL
OnlineSecurity No
OnlineBackup Yes
DeviceProtection No
TechSupport No
StreamingTV No
StreamingMovies No
Contract Month-to-month
PaperlessBilling Yes
PaymentMethod Electronic check
MonthlyCharges 29.85
TotalCharges 29.85
Churn No
contract_start 2024-11-30 00:00:00
Name: 0, dtype: object
[14]:
# Bestimme zufällig die Anzahl der Tickets (z.B. mittels einer Poisson-Verteilung)
num_tickets = np.random.poisson(lam=2)
print("Anzahl der Tickets für den Kunden:", num_tickets)
Anzahl der Tickets für den Kunden: 4
[15]:
# Wir definieren die verschiedenen Kanäle, die für den Kunden zur Verfügung steht, den Kundenservice zu erreichen
list_channels = ["Email", "Hotline", "On-site", "Chat", "Social Media"]
[16]:
# Der Einfachheitshalber definieren wir für diese Simulation die möglichen Optionen für Request-Text und Reply-Text vor:
# Liste an request-Optionen (die häufigsten Request-Gründe)
list_request = ["Problem mit Rechnung", "Internet funktioniert nicht", "Frage zum Vertrag", "Beschwerde über Kundenservice", "Technische Störung"]
# Liste an request-Optionen (die häufigsten Request-Gründe)
list_reply = ["Problem gelöst", "Weitere Informationen benötigt", "Ticket geschlossen", "Anfrage wird bearbeitet"]
[17]:
# Wir definieren die möglichen Zustände eines Tickets
list_zustand = ["gelöst", "geschlossen"]
[18]:
# Initiere eine Liste, um die Interaktionen dieses Kunden zu speichern, sowie ticket-counter
interactions = []
ticket_counter = 0
[19]:
# Für diesen ersten Testkunden erstellen wir nun für die definierte Anzahl an Tickets die Inhalte:
for t in range(num_tickets):
ticket_counter += 1
ticket_id = f"{customer_id}_T{ticket_counter}"
# Zufälliger Startzeitpunkt des Tickets zwischen Vertragsstart und Stichtag
total_days = (stichtag - contract_start).days
ticket_start_date = contract_start + timedelta(days=random.randint(0, total_days))
# Setze den initialen "last_reply_time" auf ticket_start_date, wird im Laufe des Prozesses geupdated
last_reply_time = ticket_start_date
# Bestimme zufällig, wie viele Interaktionen (1 bis 3) dieses Ticket hat
num_interactions = random.randint(1, 3)
for i in range(num_interactions):
is_original = (i == 0)
if is_original:
request_time = ticket_start_date
else:
# Für Folgeinteraktionen: Anfragezeit ist einige Tage nach der letzten Antwort,
# aber achte darauf, dass wir nicht über den Stichtag hinausgehen.
max_days = (stichtag - last_reply_time).days
# Wenn max_days 0 ist, muss es am selben Tag bleiben
additional_days = random.randint(0, 1) if max_days >= 1 else 0
request_time = last_reply_time + timedelta(days=additional_days)
# Berechne den Reply-Zeitpunkt mit der neuen Funktion
time_reply = get_reply_time(request_time, stichtag)
last_reply_time = time_reply # Update für die nächste Iteration
channel = random.choice(list_channels)
request_text = random.choice(list_request)
reply_text = random.choice(list_reply)
solved = any(keyword in reply_text.lower() for keyword in list_zustand)
interaction = {
"ticket_id": ticket_id,
"customer_id": customer_id,
"time_request": request_time,
"time_reply": time_reply,
"channel": channel,
"request": request_text,
"reply": reply_text,
"solved": solved,
"original_request": is_original,
"original_ticket_id": None if is_original else ticket_id
}
print(interaction)
interactions.append(interaction)
{'ticket_id': '7590-VHVEG_T1', 'customer_id': '7590-VHVEG', 'time_request': Timestamp('2024-12-17 00:00:00'), 'time_reply': Timestamp('2024-12-17 06:04:49'), 'channel': 'Email', 'request': 'Technische Störung', 'reply': 'Problem gelöst', 'solved': True, 'original_request': True, 'original_ticket_id': None}
{'ticket_id': '7590-VHVEG_T2', 'customer_id': '7590-VHVEG', 'time_request': Timestamp('2024-12-27 00:00:00'), 'time_reply': Timestamp('2024-12-27 04:24:40'), 'channel': 'Hotline', 'request': 'Internet funktioniert nicht', 'reply': 'Problem gelöst', 'solved': True, 'original_request': True, 'original_ticket_id': None}
{'ticket_id': '7590-VHVEG_T3', 'customer_id': '7590-VHVEG', 'time_request': Timestamp('2024-12-12 00:00:00'), 'time_reply': datetime.datetime(2024, 12, 13, 7, 28, 37), 'channel': 'On-site', 'request': 'Problem mit Rechnung', 'reply': 'Weitere Informationen benötigt', 'solved': False, 'original_request': True, 'original_ticket_id': None}
{'ticket_id': '7590-VHVEG_T3', 'customer_id': '7590-VHVEG', 'time_request': datetime.datetime(2024, 12, 14, 7, 28, 37), 'time_reply': datetime.datetime(2024, 12, 15, 8, 9, 13), 'channel': 'On-site', 'request': 'Problem mit Rechnung', 'reply': 'Problem gelöst', 'solved': True, 'original_request': False, 'original_ticket_id': '7590-VHVEG_T3'}
{'ticket_id': '7590-VHVEG_T3', 'customer_id': '7590-VHVEG', 'time_request': datetime.datetime(2024, 12, 16, 8, 9, 13), 'time_reply': datetime.datetime(2024, 12, 16, 15, 41, 19), 'channel': 'On-site', 'request': 'Technische Störung', 'reply': 'Ticket geschlossen', 'solved': True, 'original_request': False, 'original_ticket_id': '7590-VHVEG_T3'}
{'ticket_id': '7590-VHVEG_T4', 'customer_id': '7590-VHVEG', 'time_request': Timestamp('2024-12-02 00:00:00'), 'time_reply': datetime.datetime(2024, 12, 3, 17, 7, 59), 'channel': 'Chat', 'request': 'Problem mit Rechnung', 'reply': 'Ticket geschlossen', 'solved': True, 'original_request': True, 'original_ticket_id': None}
{'ticket_id': '7590-VHVEG_T4', 'customer_id': '7590-VHVEG', 'time_request': datetime.datetime(2024, 12, 4, 17, 7, 59), 'time_reply': datetime.datetime(2024, 12, 4, 18, 45, 58), 'channel': 'Email', 'request': 'Internet funktioniert nicht', 'reply': 'Ticket geschlossen', 'solved': True, 'original_request': False, 'original_ticket_id': '7590-VHVEG_T4'}
{'ticket_id': '7590-VHVEG_T4', 'customer_id': '7590-VHVEG', 'time_request': datetime.datetime(2024, 12, 4, 18, 45, 58), 'time_reply': datetime.datetime(2024, 12, 4, 23, 42, 34), 'channel': 'Email', 'request': 'Beschwerde über Kundenservice', 'reply': 'Ticket geschlossen', 'solved': True, 'original_request': False, 'original_ticket_id': '7590-VHVEG_T4'}
[20]:
# Erstelle einen DataFrame für diesen Kunden
interactions_df = pd.DataFrame(interactions)
interactions_df
[20]:
| ticket_id | customer_id | time_request | time_reply | channel | request | reply | solved | original_request | original_ticket_id | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 7590-VHVEG_T1 | 7590-VHVEG | 2024-12-17 00:00:00 | 2024-12-17 06:04:49 | Technische Störung | Problem gelöst | True | True | NaN | |
| 1 | 7590-VHVEG_T2 | 7590-VHVEG | 2024-12-27 00:00:00 | 2024-12-27 04:24:40 | Hotline | Internet funktioniert nicht | Problem gelöst | True | True | NaN |
| 2 | 7590-VHVEG_T3 | 7590-VHVEG | 2024-12-12 00:00:00 | 2024-12-13 07:28:37 | On-site | Problem mit Rechnung | Weitere Informationen benötigt | False | True | NaN |
| 3 | 7590-VHVEG_T3 | 7590-VHVEG | 2024-12-14 07:28:37 | 2024-12-15 08:09:13 | On-site | Problem mit Rechnung | Problem gelöst | True | False | 7590-VHVEG_T3 |
| 4 | 7590-VHVEG_T3 | 7590-VHVEG | 2024-12-16 08:09:13 | 2024-12-16 15:41:19 | On-site | Technische Störung | Ticket geschlossen | True | False | 7590-VHVEG_T3 |
| 5 | 7590-VHVEG_T4 | 7590-VHVEG | 2024-12-02 00:00:00 | 2024-12-03 17:07:59 | Chat | Problem mit Rechnung | Ticket geschlossen | True | True | NaN |
| 6 | 7590-VHVEG_T4 | 7590-VHVEG | 2024-12-04 17:07:59 | 2024-12-04 18:45:58 | Internet funktioniert nicht | Ticket geschlossen | True | False | 7590-VHVEG_T4 | |
| 7 | 7590-VHVEG_T4 | 7590-VHVEG | 2024-12-04 18:45:58 | 2024-12-04 23:42:34 | Beschwerde über Kundenservice | Ticket geschlossen | True | False | 7590-VHVEG_T4 |
Simuliere/Kreiere Interaktionen für alle Kunden¶
[21]:
all_interactions = []
for _, row in df.iterrows():
customer_id = row["customerID"]
contract_start = row["contract_start"]
num_tickets = np.random.poisson(lam=2)
ticket_counter = 0
for t in range(num_tickets):
ticket_counter += 1
ticket_id = f"{customer_id}_T{ticket_counter}"
num_interactions = random.randint(1, 3)
total_days = (stichtag - contract_start).days
ticket_start_date = contract_start + timedelta(days=random.randint(0, total_days))
last_reply_time = ticket_start_date
for i in range(num_interactions):
is_original = (i == 0)
if is_original:
request_time = ticket_start_date
else:
max_days = (stichtag - last_reply_time).days
additional_days = random.randint(0, 1) if max_days >= 1 else 0
request_time = last_reply_time + timedelta(days=additional_days)
time_reply = get_reply_time(request_time, stichtag)
last_reply_time = time_reply
channel = random.choice(list_channels)
request_text = random.choice(list_request)
reply_text = random.choice(list_reply)
solved = any(keyword in reply_text.lower() for keyword in list_zustand)
interaction = {
"ticket_id": ticket_id,
"customer_id": customer_id,
"time_request": request_time,
"time_reply": time_reply,
"channel": channel,
"request": request_text,
"reply": reply_text,
"solved": solved,
"original_request": is_original,
"original_ticket_id": None if is_original else ticket_id
}
all_interactions.append(interaction)
[22]:
# Erstelle den DataFrame für die simulierte "kunden-service"-Tabelle
df_kunden_service = pd.DataFrame(all_interactions)
print(df_kunden_service.shape)
df_kunden_service.head(10)
(28158, 10)
[22]:
| ticket_id | customer_id | time_request | time_reply | channel | request | reply | solved | original_request | original_ticket_id | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 7590-VHVEG_T1 | 7590-VHVEG | 2024-12-23 00:00:00 | 2024-12-23 14:28:40 | On-site | Internet funktioniert nicht | Ticket geschlossen | True | True | NaN |
| 1 | 7590-VHVEG_T1 | 7590-VHVEG | 2024-12-23 14:28:40 | 2024-12-23 20:20:22 | Hotline | Internet funktioniert nicht | Anfrage wird bearbeitet | False | False | 7590-VHVEG_T1 |
| 2 | 5575-GNVDE_T1 | 5575-GNVDE | 2023-09-03 00:00:00 | 2023-09-03 12:48:24 | Internet funktioniert nicht | Problem gelöst | True | True | NaN | |
| 3 | 5575-GNVDE_T1 | 5575-GNVDE | 2023-09-04 12:48:24 | 2023-09-05 08:04:13 | Social Media | Frage zum Vertrag | Weitere Informationen benötigt | False | False | 5575-GNVDE_T1 |
| 4 | 5575-GNVDE_T2 | 5575-GNVDE | 2024-12-16 00:00:00 | 2024-12-17 20:29:09 | On-site | Internet funktioniert nicht | Weitere Informationen benötigt | False | True | NaN |
| 5 | 5575-GNVDE_T2 | 5575-GNVDE | 2024-12-18 20:29:09 | 2024-12-19 18:25:23 | Hotline | Internet funktioniert nicht | Anfrage wird bearbeitet | False | False | 5575-GNVDE_T2 |
| 6 | 5575-GNVDE_T2 | 5575-GNVDE | 2024-12-19 18:25:23 | 2024-12-19 20:25:15 | Hotline | Internet funktioniert nicht | Anfrage wird bearbeitet | False | False | 5575-GNVDE_T2 |
| 7 | 5575-GNVDE_T3 | 5575-GNVDE | 2022-07-08 00:00:00 | 2022-07-09 12:38:29 | Social Media | Frage zum Vertrag | Problem gelöst | True | True | NaN |
| 8 | 5575-GNVDE_T3 | 5575-GNVDE | 2022-07-09 12:38:29 | 2022-07-10 20:21:07 | On-site | Beschwerde über Kundenservice | Weitere Informationen benötigt | False | False | 5575-GNVDE_T3 |
| 9 | 5575-GNVDE_T3 | 5575-GNVDE | 2022-07-11 20:21:07 | 2022-07-11 22:33:02 | Social Media | Internet funktioniert nicht | Problem gelöst | True | False | 5575-GNVDE_T3 |
[23]:
# Tabelle abspeichern, um auch anderweitig weiternutzen zu können
df_kunden_service.to_csv("kunden_service.csv", index=False)