"Statistik auf nem Mac."
=> Belastbare Erkenntnisse mittels Fakten liefern
=> Neue Erkenntnisse verständlich herausarbeiten
"Jemand, der mehr Ahnung von Statistik
hat als ein Softwareentwickler
und mehr Ahnung von Softwareentwicklung
als ein Statistiker."
Data Science & Software Data: Perfect match!
=> Krass viel!
=> vom **Problem** über die Daten zur Erkenntnis!
I. Idee
II. Laden
III. Filtern
IV. Joinen
V. Aggregieren // fun part
VI. Visualisieren // not so funny part, but...
Meta-Ziel: Alles mal sehen anhand eines einfachen Show-Cases.
Frage 1: Gibt es Module mit Teammonopole?
Unsere Heurisik: **Ändert** nur **ein Team** hauptsächlich Module?
In [1]:
from ozapfdis import git
log = git.log_numstat("../../../dropover/")
log.head(3)
Out[1]:
Was haben wir hier eigentlich?
In [2]:
log.info()
1 DataFrame (~ programmierbares Excel-Arbeitsblatt), 6 Series (= Spalten), 2403 Rows (= Einträge)
Wir machen nur mit Java-Produktionscode weiter
In [3]:
java = log.copy()
java = java[java['file'].str.startswith("backend/src/main/java")]
java = java[~java['file'].str.contains("package-info.java")]
java.head()
Out[3]:
Wir ordnen bei uns Committer zu Teams zu.
Schritt 1: Weitere Datenquelle einlesen.
In [4]:
import pandas as pd
orga = pd.read_excel("../dataset/Teamorganisation.xlsx", index_col=0)
orga
Out[4]:
Wir ordnen bei uns Committer zu Teams zu.
Schritt 2: Datenquellen joinen
In [5]:
java = java.join(orga, on='author')
java.head()
Out[5]:
Wir fassen Daten nach Modulen (=Bestandteil vom Package-Name) zusammen.
In [6]:
java['module'] = java['file'].str.split("/").str[6]
java.head()
Out[6]:
Wir markieren Dateiänderungen über neues Flag.
In [7]:
java['changed'] = 1
java.head()
Out[7]:
Wir fassen Änderungen der Klassen pro Komponenten und Team zusammen.
In [8]:
changes = java.groupby(['module', 'team'])[['changed']].sum()
changes.head()
Out[8]:
Wir berechnen alle erfolgten Änderungen pro Modul...
In [9]:
changes['all'] = changes.groupby('module').transform('sum')
changes.head()
Out[9]:
...und damit die Änderungsverhältnisse pro Team.
In [10]:
changes['ratio'] = changes['changed'] / changes['all']
changes.head()
Out[10]:
Wir bauen uns ein Balkendiagramm mit den Verhältnissen der Teamänderungen.
In [11]:
changes['ratio'].unstack().plot.bar(stacked=True);
Frage 2: Wie gut passt die Modularisierung zum Team?
Unsere Heuristik: Werden fachliche Komponenten **zusammengehörig geändert**?
Wir analysieren für alle Dateien alle Änderungen (~ Stempelkarte).
In [12]:
commit_matrix = java.pivot('file', 'sha', 'changed').fillna(0)
commit_matrix.iloc[:5,50:55]
Out[12]:
Wir berechnen den Abstand zwischen den vorgenommmenen Commits pro Datei (=Vektor)...
In [13]:
from sklearn.metrics.pairwise import cosine_distances
dis_matrix = cosine_distances(commit_matrix)
dis_matrix[:5,:5]
Out[13]:
...und machen das schöner...
In [14]:
dis_df = pd.DataFrame(
dis_matrix,
commit_matrix.index,
commit_matrix.index)
dis_df.iloc[:5,:2]
Out[14]:
...und visualisieren das Zwischenergebnis.
In [15]:
import seaborn
ax = seaborn.heatmap(dis_df, xticklabels=False, yticklabels=False);
Weiter: Wir brechen nun die mehrdimensionale Matrix auf eine (fast gleichwertige) 2D-Repräsentation...
In [16]:
from sklearn.manifold import MDS
model = MDS(dissimilarity='precomputed', random_state=0)
dis_2d = model.fit_transform(dis_df)
dis_2d[:5]
Out[16]:
...und machen das mal wieder schöner...
In [17]:
dis_2d_df = pd.DataFrame(
dis_2d,
commit_matrix.index,
["x", "y"])
dis_2d_df.head()
Out[17]:
...inkl. der Module.
In [18]:
dis_2d_df['module'] = dis_2d_df.index.str.split("/").str[6]
dis_2d_df.head()
Out[18]:
Wir erzeugen uns eine interaktive Grafik.
Helferlein: An Unifying Software Integrator
In [19]:
from ausi import pygal
xy = pygal.create_xy_chart(dis_2d_df, "module")
xy.render_in_browser()
Unsere Heuristik: Wie würde sich das System rein nach seinen **Änderungen** strukturieren?
Wir nutzen hierarchisches Clustering, um anhand der Änderungsmuster alternative Modulestrukturen zu erkennen...
In [20]:
from sklearn.cluster import AgglomerativeClustering
clustering = AgglomerativeClustering()
model = clustering.fit(commit_matrix)
model
Out[20]:
...und visualisieren das Ergebnis.
In [21]:
from ausi.scipy import plot_dendrogram
plot_dendrogram(model, labels=commit_matrix.index)
https://upload.wikimedia.org/wikipedia/commons/thumb/7/71/062-exploding-head.svg/600px-062-exploding-head.svg.png