In Unternehmen werden Datenanalysen intensiv genutzt, um aus Geschäftsdaten wertvolle Einsichten zu gewinnen. Warum nutzen wir als Softwareentwickler Datenanalysen dann nicht auch für unsere eigenen Daten?
In diesem Workshop stelle ich Vorgehen und Best Practices von Software Analytics vor. Wir sehen uns die dazugehörigen Open-Source-Werkzeuge an, mit denen sich Probleme in der Softwareentwicklung zielgerichtet analysieren und kommunizieren lassen.
Im Praxisteil mit Jupyter, pandas, jQAssistant, Neo4j & Co. erarbeiten wir gemeinsam wertvolle Einsichten aus Datenquellen wie Git-Repositories, Performancedaten, Qualitätsberichten oder auch direkt aus dem Programmcode. Wir suchen nach besonders fehleranfälligem Code, erschließen No-Go-Areas in Altanwendungen und priorisieren Aufräumarbeiten entlang wichtiger Programmteile.
Gerne kann bei diesem interaktiven Workshop direkt mitgearbeitet werden. Ein Notebook mit Internetzugang reicht hierfür völlig aus.
%matplotlib inline
import pandas as pd
Markus Harrer, Software Development Analyst
@feststelltaste
ML Summit 2019, 14. Oktober 2019
"Software Analytics is analytics on software data for managers and software engineers with the aim of empowering software development individuals and teams to gain and share insight from their data to make better decisions."
Alles was aus der Entwicklung und dem Betrieb der Softwaresysteme so anfällt:
Sehr große Auswahl == sehr große Möglichkeiten?
Thomas Zimmermann in "One size does not fit all":
Aber: "... the methods typically are applicable on different datasets."
=> Analyseideen sind wiederverwendbar!
Individuelle Systeme == Individuelle Probleme => Individuelle Analysen => Individuelle Erkenntnisse!
"Statistics on a Mac."
=> Belastbare Erkenntnisse mittels Fakten liefern
"The aim of science is to seek the simplest explanations of complex facts."
=> Neue Erkenntnisse verständlich herausarbeiten
pd.read_csv("../datasets/google_trends_datascience.csv").plot();
"100" == max. Beliebtheit!
"A data scientist is someone who
is better at statistics
than any software engineer
and better at software engineering
than any statistician."
Nicht so weit weg wie gedacht!
Roger Pengs "Stages of Data Analysis"
I. Fragestellung
II. Explorative Datenanalyse
III. Formale Modellierung
IV. Interpretation
V. Kommunikation
=> von der Frage über die Daten zur Erkenntnis!
...of inductive software engineering" (Tim Menzies)
(Intent + Code + Data + Results)
* Logical Step
+ Automation
= Literate Statistical Programming
Vehikel: Computational notebooks
![]() |
Pro Variable eine Spalte |
![]() |
Für jede Beobachtung eine Reihe |
![]() |
Für alle zusammengehörigen Variablen eine Tabelle |
![]() |
Für jede Tabelle einer Analyse eine verlinkende Spalte |
Interactive Notebook
=> Neue Erkenntnisse verständlich herausarbeiten!
Eine beliebte Programmiersprache im Data Science
=> Datenanalysen werden wiederholbar
Pragmatisches Datenanalysewerkzeug
=> Guter Integrationspunkt für Datenquellen!
Progammierbare Visualisierungsbibliothek
=> Direkte Visualisierung der Diagramme / Ergebnisse!
=> Bietet in ganz individuellen Situationen die notwendige Flexibilität!
Jupyter Notebook arbeitet auch mit anderen Technologieplattformen zusammen, z. B. mit
=> Spezielle Technologie? Wird (meist) unterstützt!
https://www.feststelltaste.de/category/top5/
Kurse, Videos, Blogs, Bücher und mehr...
*einige Seiten befinden sich noch in der Entwicklung
https://github.com/feststelltaste/software-analytics-workshop
Frage
Meta-Ziel: Grundmechaniken kennenlernen.
Wir laden einen Datenexport aus einem Git-Repository.
log = pd.read_csv("../datasets/git_log_intellij.csv.gz")
log.head()
additions | deletions | filename | sha | timestamp | author | |
---|---|---|---|---|---|---|
0 | 4 | 0 | java/java-impl/src/com/intellij/codeInsight/hi... | be6247932aa9 | 2019-07-01 14:40:24 | Roman.Ivanov |
1 | 4 | 6 | java/java-impl/src/com/intellij/codeInsight/hi... | ee2032b77eca | 2019-07-01 11:36:14 | Roman.Ivanov |
2 | 0 | 3 | java/java-impl/src/META-INF/JavaPlugin.xml | fbeb4d639dc1 | 2019-06-26 11:14:34 | Roman.Ivanov |
3 | 6 | 3 | java/java-impl/src/com/intellij/codeInsight/hi... | fbeb4d639dc1 | 2019-06-26 11:14:34 | Roman.Ivanov |
4 | 11 | 6 | java/java-impl/src/com/intellij/codeInsight/hi... | a3d5a9b855fe | 2019-06-14 10:32:15 | Roman.Ivanov |
Wir sehen uns Basisinfos über den Datensatz an.
log.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 1128819 entries, 0 to 1128818 Data columns (total 6 columns): additions 1128819 non-null object deletions 1128819 non-null object filename 1128819 non-null object sha 1128819 non-null object timestamp 1128819 non-null object author 1128819 non-null object dtypes: object(6) memory usage: 51.7+ MB
1 DataFrame (~ programmierbares Excel-Arbeitsblatt), 6 Series (= Spalten), 1128819 entries (= Reihen)
Wir wandeln die Zeitstempel von Texte in Objekte um.
log['timestamp'] = pd.to_datetime(log['timestamp'])
log.head()
additions | deletions | filename | sha | timestamp | author | |
---|---|---|---|---|---|---|
0 | 4 | 0 | java/java-impl/src/com/intellij/codeInsight/hi... | be6247932aa9 | 2019-07-01 14:40:24 | Roman.Ivanov |
1 | 4 | 6 | java/java-impl/src/com/intellij/codeInsight/hi... | ee2032b77eca | 2019-07-01 11:36:14 | Roman.Ivanov |
2 | 0 | 3 | java/java-impl/src/META-INF/JavaPlugin.xml | fbeb4d639dc1 | 2019-06-26 11:14:34 | Roman.Ivanov |
3 | 6 | 3 | java/java-impl/src/com/intellij/codeInsight/hi... | fbeb4d639dc1 | 2019-06-26 11:14:34 | Roman.Ivanov |
4 | 11 | 6 | java/java-impl/src/com/intellij/codeInsight/hi... | a3d5a9b855fe | 2019-06-14 10:32:15 | Roman.Ivanov |
Wir sehen uns nur die jüngsten Änderungen an.
# use log['timestamp'].max() instead of pd.Timedelta('today') to avoid outdated data in the future
recent = log[log['timestamp'] > log['timestamp'].max() - pd.Timedelta('90 days')]
recent.head()
additions | deletions | filename | sha | timestamp | author | |
---|---|---|---|---|---|---|
0 | 4 | 0 | java/java-impl/src/com/intellij/codeInsight/hi... | be6247932aa9 | 2019-07-01 14:40:24 | Roman.Ivanov |
1 | 4 | 6 | java/java-impl/src/com/intellij/codeInsight/hi... | ee2032b77eca | 2019-07-01 11:36:14 | Roman.Ivanov |
2 | 0 | 3 | java/java-impl/src/META-INF/JavaPlugin.xml | fbeb4d639dc1 | 2019-06-26 11:14:34 | Roman.Ivanov |
3 | 6 | 3 | java/java-impl/src/com/intellij/codeInsight/hi... | fbeb4d639dc1 | 2019-06-26 11:14:34 | Roman.Ivanov |
4 | 11 | 6 | java/java-impl/src/com/intellij/codeInsight/hi... | a3d5a9b855fe | 2019-06-14 10:32:15 | Roman.Ivanov |
Wir wollen nur Java-Code verwenden.
java = recent[recent['filename'].str.endswith(".java")].copy()
java.head()
additions | deletions | filename | sha | timestamp | author | |
---|---|---|---|---|---|---|
27 | 1 | 10 | platform/smRunner/src/com/intellij/execution/t... | f4ed78c8f574 | 2019-06-28 18:28:41 | Ilya.Kazakevich |
29 | 28 | 0 | platform/smRunner/testSrc/com/intellij/executi... | f4ed78c8f574 | 2019-06-28 18:28:41 | Ilya.Kazakevich |
30 | 6 | 3 | plugins/InspectionGadgets/InspectionGadgetsAna... | a724467ad1a5 | 2019-07-01 19:47:38 | Roman Shevchenko |
31 | 2 | 2 | plugins/InspectionGadgets/test/com/siyeh/igfix... | a724467ad1a5 | 2019-07-01 19:47:38 | Roman Shevchenko |
32 | 2 | 2 | plugins/InspectionGadgets/test/com/siyeh/igfix... | a724467ad1a5 | 2019-07-01 19:47:38 | Roman Shevchenko |
Wir zählen die Anzahl der Änderungen je Datei.
changes = java.groupby('filename')[['sha']].count()
changes.head()
sha | |
---|---|
filename | |
RegExpSupport/gen/org/intellij/lang/regexp/_RegExLexer.java | 1 |
RegExpSupport/src/org/intellij/lang/regexp/RegExpCapability.java | 1 |
RegExpSupport/src/org/intellij/lang/regexp/RegExpFileType.java | 5 |
RegExpSupport/src/org/intellij/lang/regexp/RegExpLanguageHost.java | 16 |
RegExpSupport/src/org/intellij/lang/regexp/RegExpLanguageHosts.java | 15 |
Wir holen Infos über die Code-Zeilen hinzu...
loc = pd.read_csv("../datasets/cloc_intellij.csv.gz", index_col=1)
loc.head()
language | blank | comment | code | |
---|---|---|---|---|
filename | ||||
java/java-tests/testData/psi/resolve/ThinletBig.java | Java | 299 | 1140 | 20125 |
java/java-tests/testData/psi/parser-full/declarationParsing/class/LongClass.java | Java | 10121 | 10164 | 10166 |
python/gen/com/jetbrains/python/console/protocol/PythonConsoleBackendService.java | Java | 1971 | 591 | 10086 |
jps/jps-builders/src/org/jetbrains/jps/api/CmdlineRemoteProto.java | Java | 502 | 3066 | 8605 |
plugins/java-decompiler/engine/testData/obfuscated/aj.java | Java | 551 | 1 | 8043 |
...und verschneiden diese mit den vorhandenen Daten.
hotspots = changes.join(loc[['code']]).dropna(subset=['code'])
hotspots.head()
sha | code | |
---|---|---|
filename | ||
RegExpSupport/gen/org/intellij/lang/regexp/_RegExLexer.java | 1 | 1190.0 |
RegExpSupport/src/org/intellij/lang/regexp/RegExpCapability.java | 1 | 34.0 |
RegExpSupport/src/org/intellij/lang/regexp/RegExpFileType.java | 5 | 40.0 |
RegExpSupport/src/org/intellij/lang/regexp/RegExpLanguageHost.java | 16 | 92.0 |
RegExpSupport/src/org/intellij/lang/regexp/RegExpLanguageHosts.java | 15 | 168.0 |
Wir zeigen nur die TOP 10 Hotspots im Code an.
top10 = hotspots.sort_values(by="sha", ascending=False).head(10)
top10
sha | code | |
---|---|---|
filename | ||
platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/StructuralSearchDialog.java | 83 | 1097.0 |
platform/platform-impl/src/com/intellij/idea/IdeaApplication.java | 71 | 366.0 |
platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java | 67 | 1425.0 |
platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java | 65 | 810.0 |
platform/lang-impl/src/com/intellij/build/BuildTreeConsoleView.java | 63 | 936.0 |
java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/TrackingRunner.java | 63 | 1300.0 |
platform/platform-impl/src/com/intellij/idea/StartupUtil.java | 62 | 551.0 |
platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerConfigurableNewLayout.java | 60 | 1284.0 |
platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaComboBoxUI.java | 59 | 606.0 |
platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java | 57 | 299.0 |
Wir erzeugen ein XY-Diagramm aus der TOP 10 Liste.
ax = top10.plot.scatter('sha', 'code');
for k, v in top10.iterrows():
ax.annotate(k.split("/")[-1], v)
vmstat
jdeps
und Visualisierung mit D3
Git
jQAssistant
/ Neo4j
1. Softwareanalysen mit Data-Science-Werkzeugen sind möglich
2. Wer mehr will bekommt auch mehr!
3. Es gibt unglaublich viele Quellen für Daten in der Softwareentwicklung
=> von der Frage über die Daten zur Erkenntnis!
GitHub-Repository: https://git.io/Jelju