Initial commit

This commit is contained in:
ngb 2020-11-26 08:18:58 +01:00
commit b526997439
16 changed files with 3239 additions and 0 deletions

26
.gitignore vendored Normal file
View File

@ -0,0 +1,26 @@
# ---> Java
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
Dokumente*
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

210
BinaryTree.java Normal file
View File

@ -0,0 +1,210 @@
/**
* <p>
* Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
* </p>
* <p>
* Generische Klasse BinaryTree<ContentType>
* </p>
* <p>
* Mithilfe der generischen Klasse BinaryTree koennen beliebig viele
* Inhaltsobjekte vom Typ ContentType in einem Binaerbaum verwaltet werden. Ein
* Objekt der Klasse stellt entweder einen leeren Baum dar oder verwaltet ein
* Inhaltsobjekt sowie einen linken und einen rechten Teilbaum, die ebenfalls
* Objekte der generischen Klasse BinaryTree sind.
* </p>
*
* @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
* @version Generisch_03 2014-03-01
*/
public class BinaryTree<ContentType> {
/* --------- Anfang der privaten inneren Klasse -------------- */
/**
* Durch diese innere Klasse kann man dafuer sorgen, dass ein leerer Baum
* null ist, ein nicht-leerer Baum jedoch immer eine nicht-null-Wurzel sowie
* nicht-null-Teilbaeume, ggf. leere Teilbaeume hat.
*/
private class BTNode<CT> {
private CT content;
private BinaryTree<CT> left, right;
public BTNode(CT pContent) {
// Der Knoten hat einen linken und einen rechten Teilbaum, die
// beide von null verschieden sind. Also hat ein Blatt immer zwei
// leere Teilbaeume unter sich.
this.content = pContent;
left = new BinaryTree<CT>();
right = new BinaryTree<CT>();
}
}
/* ----------- Ende der privaten inneren Klasse -------------- */
private BTNode<ContentType> node;
/**
* Nach dem Aufruf des Konstruktors existiert ein leerer Binaerbaum.
*/
public BinaryTree() {
this.node = null;
}
/**
* Wenn der Parameter pContent ungleich null ist, existiert nach dem Aufruf
* des Konstruktors der Binaerbaum und hat pContent als Inhaltsobjekt und
* zwei leere Teilbaeume. Falls der Parameter null ist, wird ein leerer
* Binaerbaum erzeugt.
*
* @param pContent
* das Inhaltsobjekt des Wurzelknotens vom Typ ContentType
*/
public BinaryTree(ContentType pContent) {
if (pContent != null) {
this.node = new BTNode<ContentType>(pContent);
} else {
this.node = null;
}
}
/**
* Wenn der Parameter pContent ungleich null ist, wird ein Binaerbaum mit
* pContent als Inhalt und den beiden Teilbaeume pLeftTree und pRightTree
* erzeugt. Sind pLeftTree oder pRightTree gleich null, wird der
* entsprechende Teilbaum als leerer Binaerbaum eingefuegt. So kann es also
* nie passieren, dass linke oder rechte Teilbaeume null sind. Wenn der
* Parameter pContent gleich null ist, wird ein leerer Binaerbaum erzeugt.
*
* @param pContent
* das Inhaltsobjekt des Wurzelknotens vom Typ ContentType
* @param pLeftTree
* der linke Teilbaum vom Typ BinaryTree<ContentType>
* @param pRightTree
* der rechte Teilbaum vom Typ BinaryTree<ContentType>
*/
public BinaryTree(ContentType pContent, BinaryTree<ContentType> pLeftTree, BinaryTree<ContentType> pRightTree) {
if (pContent != null) {
this.node = new BTNode<ContentType>(pContent);
if (pLeftTree != null) {
this.node.left = pLeftTree;
} else {
this.node.left = new BinaryTree<ContentType>();
}
if (pRightTree != null) {
this.node.right = pRightTree;
} else {
this.node.right = new BinaryTree<ContentType>();
}
} else {
// Da der Inhalt null ist, wird ein leerer BinarySearchTree erzeugt.
this.node = null;
}
}
/**
* Diese Anfrage liefert den Wahrheitswert true, wenn der Binaerbaum leer
* ist, sonst liefert sie den Wert false.
*
* @return true, wenn der Binaerbaum leer ist, sonst false
*/
public boolean isEmpty() {
return this.node == null;
}
/**
* Wenn pContent null ist, geschieht nichts. <br />
* Ansonsten: Wenn der Binaerbaum leer ist, wird der Parameter pContent als
* Inhaltsobjekt sowie ein leerer linker und rechter Teilbaum eingefuegt.
* Ist der Binaerbaum nicht leer, wird das Inhaltsobjekt durch pContent
* ersetzt. Die Teilbaeume werden nicht geaendert.
*
* @param pContent
* neues Inhaltsobjekt vom Typ ContentType
*/
public void setContent(ContentType pContent) {
if (pContent != null) {
if (this.isEmpty()) {
node = new BTNode<ContentType>(pContent);
this.node.left = new BinaryTree<ContentType>();
this.node.right = new BinaryTree<ContentType>();
}
this.node.content = pContent;
}
}
/**
* Diese Anfrage liefert das Inhaltsobjekt des Binaerbaums. Wenn der
* Binaerbaum leer ist, wird null zurueckgegeben.
*
* @return das Inhaltsobjekt der Wurzel vom Typ ContentType bzw. null, wenn
* der Binaerbaum leer ist
*/
public ContentType getContent() {
if (this.isEmpty()) {
return null;
} else {
return this.node.content;
}
}
/**
* Falls der Parameter null ist, geschieht nichts. Wenn der Binaerbaum leer
* ist, wird pTree nicht angehaengt. Andernfalls erhaelt der Binaerbaum den
* uebergebenen BinaryTree als linken Teilbaum.
*
* @param pTree
* neuer linker Teilbaum vom Typ BinaryTree<ContentType>
*/
public void setLeftTree(BinaryTree<ContentType> pTree) {
if (!this.isEmpty() && pTree != null) {
this.node.left = pTree;
}
}
/**
* Falls der Parameter null ist, geschieht nichts. Wenn der Binaerbaum leer
* ist, wird pTree nicht angehaengt. Andernfalls erhaelt der Binaerbaum den
* uebergebenen BinaryTree als rechten Teilbaum.
*
* @param pTree
* neuer linker Teilbaum vom Typ BinaryTree<ContentType>
*/
public void setRightTree(BinaryTree<ContentType> pTree) {
if (!this.isEmpty() && pTree != null) {
this.node.right = pTree;
}
}
/**
* Diese Anfrage liefert den linken Teilbaum des Binaerbaumes. Wenn der
* Binaerbaum leer ist, wird null zurueckgegeben.
*
* @return linker Teilbaum vom Typ BinaryTree<ContentType> oder null, wenn
* der aktuelle Binaerbaum leer ist
*/
public BinaryTree<ContentType> getLeftTree() {
if (!this.isEmpty()) {
return this.node.left;
} else {
return null;
}
}
/**
* Diese Anfrage liefert den rechten Teilbaum des Binaerbaumes. Wenn der
* Binaerbaum (this) leer ist, wird null zurueckgegeben.
*
* @return rechter Teilbaum vom Typ BinaryTree<ContentType> oder null, wenn
* der aktuelle Binaerbaum (this) leer ist
*/
public BinaryTree<ContentType> getRightTree() {
if (!this.isEmpty()) {
return this.node.right;
} else {
return null;
}
}
}

22
Classification.java Normal file
View File

@ -0,0 +1,22 @@
/**
* Klassifikationen sind Blattknoten im Entschiedungsbaum, die
* einem Passagier eine Klasse zuweisen. Diese Klasse ist statisch, also
* für jeden Datensatz gleich, und wird im Konstruktor festgelegt.
*/
public class Classification extends DecisionNode {
private String classification;
public Classification( String pClass ) {
classification = pClass;
}
public String decide( Passenger pPassenger ) {
return classification;
}
public String toString() {
return "Classification[" + classification + "]";
}
}

34
Decision.java Normal file
View File

@ -0,0 +1,34 @@
/**
* Entschiedungen sind die Knoten im Entschiedungsbaum. Sie
* entschieden für einen Passagier, ob im linken oder rechten
* Teilbaum weitergesucht werden muss.
* <p>
* Entscheidungen sind binär und werden durch den Namen des Attributs
* und den Wert für den linken Teilbaum festgelegt. Die Entschiedung ist
* dann also "links", falls das Attribut des Datensatzes den festgelegten
* Wert hat und "rechts" für alle anderen.
*/
public class Decision extends DecisionNode {
private String attribute;
private String valueLeft;
public Decision( String pAttribute, String pValueLeft ) {
attribute = pAttribute;
valueLeft = pValueLeft;
}
public String decide( Passenger pPassenger ) {
if( pPassenger.getValue(attribute).equals(valueLeft) ) {
return "left";
} else {
return "right";
}
}
public String toString() {
return "Decision[attribute:" + attribute + ",left:" + valueLeft + ",right:not " + valueLeft + "]";
}
}

13
DecisionNode.java Normal file
View File

@ -0,0 +1,13 @@
/**
* Ein Knoten im Entschiedungsbaum. Knoten sind Entschiedungen,
* die für einen Passagier entscheiden, ob im linken oder
* rechten Teilbaum weitergesucht werden muss, oder dem Passagier
* eine Klasse zuweisen (Blattknoten).
*/
public abstract class DecisionNode {
public abstract String decide( Passenger pPassenger );
public abstract String toString();
}

352
DecisionTreeBuilder.java Normal file
View File

@ -0,0 +1,352 @@
public class DecisionTreeBuilder {
public static final String TRAININGDATA = "titanic50.csv";
//public static final String TRAININGDATA = "titanic300.csv";
//public static final String TRAININGDATA = "titanic800.csv";
public static final String TESTDATA = "titanicTest.csv";
private int maxDepth;
private double minInformationgain;
private double minEntropy;
private BinaryTree<DecisionNode> decisionTree;
/**
* Setzt die maximale Tiefe auf <code>3</code>.
*/
public DecisionTreeBuilder() {
//this(3, 0.25, 0.5);
this(3);
}
public DecisionTreeBuilder( int pMaxDepth ) {
maxDepth = pMaxDepth;
minInformationgain = 0.0;
minEntropy = 0.0;
}
/*
public DecisionTreeBuilder( double pMinInformationgain ) {
this(Integer.MAX_VALUE, pMinInformationgain, 0.0);
}
public DecisionTreeBuilder( double pMinInformationgain, double pMinEntropy ) {
this(Integer.MAX_VALUE, pMinInformationgain, pMinEntropy);
}
public DecisionTreeBuilder( int pMaxDepth, double pMinInformationgain, double pMinEntropy ) {
maxDepth = pMaxDepth;
minInformationgain = pMinInformationgain;
minEntropy = pMinEntropy;
}
*/
/**
* Erstellt einen Entscheidungsbaum aus den {@link #TRAININGDATA Trainingsdaten}.
*/
public void buildTree() {
buildTree(TRAININGDATA);
}
/**
* Liest die Trainingsdaten aus der Datei <var>pFilename</var> und erstellt
* den Entscheidungsbaum.
*
* @param pFilename CSV-Datei im Projektordner
*/
public void buildTree( String pFilename ) {
List<Passenger> trainingdata = loadData(pFilename);
System.out.println("Fertig: Daten geladen");
decisionTree = buildTree(trainingdata);
System.out.println("Fertig: Entschiedungsbaum erstellt");
}
/**
* Gibt den Baum als Text auf der Kommandozeile aus.
*/
public void printTree() {
if( decisionTree == null ) {
System.out.println("Fehler: Noch kein Baum erstellt!");
} else {
printTree(decisionTree, 0);
}
}
/**
*
*/
/**
* Gibt den Baum auf der {@link System#out Kommandozeile} aus.
* <p>
* Der Baum wird von der Wurzel rekursiv druchlaufen.
* Zuerst wird der aktuelle Knoten ausgegeben, dann die
* Knoten des linken Teilbaumes und schließlich die
* Knoten des rechten Teilbaumes.
* </p>
*
* @param pTree Der aktuelle Teilbaum
* @param pDepth Die aktuelle Tiefe
*/
private void printTree( BinaryTree<DecisionNode> pTree, int pDepth ) {
// Aktueller Inhalt der Wurzel des Teilbaumes
DecisionNode k = pTree.getContent();
// TODO: Ausgabe des Baumes implemeniteren
// Mit k.toString() kann der Inhalt
// des Knotens als String geholt und mit
// System.out.println() ausgeben werden.
// Beispielhafter Aufruf für die Rekursion
// im linken Teilbaum.
// baumAusgeben(pBaum.getLeftTree(), pTiefe+1);
}
/**
* Klassifiziert die {@link #TESTDATA Testdaten} im Baum und
* vergleicht das Ergebnis mit dem tatsächlich in den Testdaten
* vorhandenen.
*/
public void classifyTestdata() {
classifyTestdata(TESTDATA);
}
/**
* Klassifiziert die Testdaten in der angegebenen Datei im Baum und
* vergleicht das Ergebnis mit dem tatsächlich in den Testdaten
* vorhandenen.
*/
public void classifyTestdata( String pFilename ) {
if( decisionTree == null ) {
System.out.println("Fehler: Noch kein Baum erstellt!");
} else {
List<Passenger> testdata = loadData(pFilename);
double errors = 0, total = 0;
testdata.toFirst();
while( testdata.hasAccess() ) {
Passenger p = testdata.getContent();
String classification = classifyPassenger(p, decisionTree);
String expected = "überlebt";
if( p.survived == 0 ) {
expected = "verstorben";
}
System.out.printf("%s %s ", p.name, classification);
if( !classification.equals(expected) ) {
System.out.println("(Fehler)");
errors++;
} else {
System.out.println("(Korrekt)");
}
total++;
testdata.next();
}
System.out.printf("\nTest fertig: %s von %s Fehler (%s%%)",
errors, total, Math.round(errors / total * 100));
}
}
/**
* Klassifiziert einen Passagier im Baum <var>pTree</var> und gibt das Ergebnis zurück.
*
* @param pPassenger Der zu prüfende Passagier
* @param pTree Der (Teil-)Baum, in dem klassifiziert werden soll.
* @return "verstorben" oder "überlebt"
*/
private String classifyPassenger( Passenger pPassenger, BinaryTree<DecisionNode> pTree ) {
String answer = pTree.getContent().decide(pPassenger);
if( answer.equals("left") ) {
return classifyPassenger(pPassenger, pTree.getLeftTree());
} else if( answer.equals("right") ) {
return classifyPassenger(pPassenger, pTree.getRightTree());
} else {
return answer;
}
}
/**
* Erstellt einen Entschedungsbaum zu den angegebenen Trainingsdaten mittels des
* ID3-Algorithmus.
*
* <p>
* Es wird immer ein binärer Entscheidungsbaum erstellt. Das bedeutet, Attribute
* mit drei möglcihen Ausprägungen <code>w1</code>, <code>w2</code> und <code>w3</code>
* werden in zwei Entschiedungen aufgeteilt, die zunächst unter <code>==w1</code> und
* <code>!=w1</code> unterscheiden und dann in <code>==w2</code> und <code>==w3</code>.
* </p>
*
* @param trainingdata Die Liste mit Passagieren, die für das Trining genutzt werden soll.
* @return Der Entschiedungsbaum.
* @see #loadData(String)
*/
public BinaryTree<DecisionNode> buildTree( List<Passenger> trainingdata ) {
return buildTree(trainingdata, 0);
}
private BinaryTree<DecisionNode> buildTree( List<Passenger> trainingdata, int pDepth ) {
ID3 id3 = count(trainingdata);
String bestAttribute = bestAttribute(id3);
String[] attributeValues = Passenger.getValues(bestAttribute);
if( id3.entropie() < minEntropy || bestAttribute.equals("") || pDepth >= maxDepth ) {
BinaryTree<DecisionNode> klasse = new BinaryTree<>();
if( id3.getRatio(1) >= id3.getRatio(0) ) {
klasse.setContent(new Classification("überlebt"));
} else {
klasse.setContent(new Classification("verstorben"));
}
return klasse;
} else {
Decision e = new Decision(bestAttribute, attributeValues[0]);
BinaryTree<DecisionNode> eBaum = new BinaryTree<>(e);
eBaum.setLeftTree(buildTree(filterData(bestAttribute, attributeValues[0], trainingdata), pDepth + 1));
if( attributeValues.length == 2 ) {
eBaum.setRightTree(buildTree(filterData(bestAttribute, attributeValues[1], trainingdata), pDepth + 1));
} else {
BinaryTree<DecisionNode> tmpTree = new BinaryTree<>();
tmpTree.setContent(new Decision(bestAttribute, attributeValues[1]));
eBaum.setRightTree(tmpTree);
tmpTree.setLeftTree(buildTree(filterData(bestAttribute, attributeValues[1], trainingdata), pDepth + 1));
tmpTree.setRightTree(buildTree(filterData(bestAttribute, attributeValues[2], trainingdata), pDepth + 1));
}
return eBaum;
}
}
private String bestAttribute( ID3 id3Counts ) {
String[] attributes = Passenger.getAttribute();
String bestAttribut = "";
double maxInformation = 0;
for( int i = 0; i < attributes.length; i++ ) {
double ig = id3Counts.informationgain(attributes[i]);
if( ig > maxInformation && ig > minInformationgain ) {
bestAttribut = attributes[i];
maxInformation = ig;
}
}
return bestAttribut;
}
private ID3 count( List<Passenger> trainingdata ) {
ID3 id3Counter = new ID3();
trainingdata.toFirst();
while( trainingdata.hasAccess() ) {
id3Counter.count(trainingdata.getContent());
trainingdata.next();
}
return id3Counter;
}
/**
* Filtert die Liste von Passagieren nach einer Attribut/Wert Kombination.
*
* @param pAttribute Attributname, nach dem gefiltert wird
* @param pValue Attributwert, nach dem gefiltert wird
* @param trainingdata Zu filternde Datensätze
* @return
*/
private List<Passenger> filterData( String pAttribute, String pValue, List<Passenger> trainingdata ) {
List<Passenger> filteredData = new List<Passenger>();
trainingdata.toFirst();
while( trainingdata.hasAccess() ) {
Passenger p = trainingdata.getContent();
if( p.getValue(pAttribute).equals(pValue) ) {
filteredData.append(p);
}
trainingdata.next();
}
return filteredData;
}
/**
* Lädt die angegebene Textdatei in eine Liste von {@link Passenger Passagieren}.
* Die Datei muss eine durcg ";" getrennte CSV-Datei sein, deren Spalten so aufgebaut sind:
*
* <pre>
* ID;class;survived;name,sex;age;sibsp;parch;embarked
* </pre>
* <p>
* Die Attribute <code>class</code>, <code>survived</code>, <code>sibsp</code>
* und <code>parch</code> werden als Integer geparsed.
*
* @param pDatei
* @return
*/
private List<Passenger> loadData( String pDatei ) {
List<Passenger> data = new List<Passenger>();
List<String> lines = FileSystem.getFileContents(pDatei);
lines.toFirst();
while( lines.hasAccess() ) {
String[] lineData = lines.getContent().trim().split(";");
if( lineData.length < 9 ) {
System.err.println(lines.getContent());
}
Passenger p = new Passenger(lineData[3],
lineData[5], lineData[4], lineData[8],
Integer.valueOf(lineData[1].trim()),
Integer.valueOf(lineData[6].trim()),
Integer.valueOf(lineData[7].trim()),
Integer.valueOf(lineData[2].trim()));
data.append(p);
lines.next();
}
return data;
}
/**
* Lädt die {@link #TRAININGDATA Trainingsdaten} und berechnet die
* Kenngrößen des ID3-Algorithmus: Entropien und Inforamtionsgewinne.
*
* @see ID3
*/
public void calculateEntropy() {
calculateEntropy(TRAININGDATA);
}
/**
* Lädt die Daten aus der angegebenen Datei und berechnet die
* Kenngrößen des ID3-Algorithmus: Entropien und Inforamtionsgewinne.
*
* @see ID3
* @see #loadData(String)
*/
public void calculateEntropy( String pFilename ) {
List<Passenger> trainingdata = loadData(pFilename);
System.out.println("Fertig: Daten geladen");
ID3 id3 = count(trainingdata);
System.out.printf("E_gesamt = %s\n", id3.entropie());
String[] attrs = Passenger.getAttribute();
for( int i = 0; i < attrs.length; i++ ) {
String attr = attrs[i];
String[] values = Passenger.getValues(attr);
for( int j = 0; j < values.length; j++ ) {
String value = values[j];
System.out.printf("E_%s(%s) = %s\n", attr, value, id3.entropie(attr, value));
}
}
for( int i = 0; i < attrs.length; i++ ) {
String attr = attrs[i];
System.out.printf("I_%s = %s\n", attr, id3.informationgain(attr));
}
}
}

280
FileSystem.java Normal file
View File

@ -0,0 +1,280 @@
import java.io.*;
/**
* Hilfsklasse um auf einfache Weise mit dem Dateisystem zu interagieren.
* Die Klasse versteckt die Komplexität hinter einfachen Methoden und
* ist nutzt so weit es geht die Klassen der Abiturvorgaben NRW.
* @version 1.1 (2019-09-13)
* @author J. Neugebauer <schule@neugebauer.cc>
*/
public class FileSystem {
/**
* Klassenmethode um eine Textdatei im gleichen Verzeichnis
* wie die FileSystem-Klasse zeilenweise in eine Liste zu lesen.
* Im Fehlerfall oder wenn die Datei nicht existiert ist das Ergebnis
* eine leere Liste.
*
* @param pFilename
* @return
*/
public static List<String> getFileContents( String pFilename ) {
try {
File f = new File(FileSystem.class.getResource(pFilename).toURI());
if( f.isFile() ) {
FileSystem fs;
fs = new FileSystem(f.getParent());
return fs.readLines(f.getName());
} else {
return new List<String>();
}
} catch( Exception ex ) {
return new List<String>();
}
}
// Wurzelverzeichnis dieses Dateisystems.
private File root;
// Aktuelles Verzeichnis, nachdem das Verzeichnis gewechselt wurde.
private File currentDir;
/**
* Erstellt ein Dateisystem relativ zum angegebenen Wurzelordner.
*
* @param pRoot
*/
public FileSystem( String pRoot ) {
root = new File(pRoot);
root = root.getAbsoluteFile();
currentDir = root;
}
/**
* Erstellt ein Dateisystem relativ zum angegebenen Wurzelordner.
*
* @param pRoot
*/
public FileSystem( File pRoot ) {
root = pRoot.getAbsoluteFile();
currentDir = root;
}
/**
* Erstellt ein Dateisystem relativ zum aktuellen Arbeitsverzeichnis.
*/
public FileSystem() {
root = new File(".").getAbsoluteFile().getParentFile();
currentDir = root;
}
/**
* Liest eine Datei zeilenweise in eine Liste von Strings. Existiert die
* Datei nicht oder kann nicht gelesen werden wird eine leere Liste erzeugt.
*
* @param pFilename Dateiname relativ zum aktuellen Verzeichnis.
* @return
*/
public List<String> readLines( String pFilename ) {
File f = new File(currentDir, pFilename);
if( f.exists() && f.isFile() && f.canRead() ) {
List<String> lines = new List<String>();
try {
BufferedReader br = new BufferedReader(new FileReader(f));
try {
String line = br.readLine();
while( line != null ) {
lines.append(line);
line = br.readLine();
}
} catch( IOException ex ) {
} finally {
br.close();
}
} catch( IOException ex ) {
}
return lines;
} else {
return new List<String>();
}
}
/**
* Liest eine Datei als String ein. Existiert die
* Datei nicht oder kann nicht gelesen werden wird ein
* leerer String zurück gegeben.
*
* @param pFilename Dateiname relativ zum aktuellen Verzeichnis.
* @return
*/
public String readFile( String pFilename ) {
String content = "";
List<String> lines = readLines(pFilename);
lines.toFirst();
while( lines.hasAccess() ) {
content += lines.getContent() + "\n";
}
return content;
}
/**
* Gibt eine Liste von Strings zurück, die die Namen aller Dateien
* im aktuellen Verzeichnis enthält.
*
* @return
*/
public List<String> getFileList() {
List<String> files = new List<String>();
currentDir.listFiles(new FileFilter() {
@Override
public boolean accept( File f ) {
if( f.isFile() && !f.isHidden() ) {
files.append(f.getName());
}
return false;
}
});
return files;
}
/**
* Gibt eine Liste von Strings zurück, die die Namen aller Ordner
* im aktuellen Verzeichnis enthält.
*
* @return
*/
public List<String> getDirectoryList() {
List<String> directories = new List<String>();
currentDir.listFiles(new FileFilter() {
@Override
public boolean accept( File f ) {
if( f.isDirectory() && !f.isHidden() ) {
directories.append(f.getName());
}
return false;
}
});
return directories;
}
/**
* Gibt den absoluten Pfad des aktuellen Verzeichnisses
* zurück.
*
* @return
*/
public String getPath() {
return currentDir.getAbsolutePath();
}
/**
* Gibt den absoluten Pfad des Wurzelverzeichnisses zurück.
*
* @return
*/
public String getRootPath() {
return root.getAbsolutePath();
}
/**
* Navigiert in das Elternverzeichnis des aktuellen Verzeichnisses,
* wenn möglich. Falls das aktuelle Verzeichnis das Wurzelverzeichnis
* ist oder es kein Elternverzeichnis gibt, ändert sich das aktuelle
* Verzeichnis nicht.
*
* @return <code>true</code> - Verzeichnis geändert, <code>false</code> - Navigation nicht möglich
*/
public boolean cdUp() {
if( currentDir.equals(root) ) {
return false;
} else {
currentDir = currentDir.getParentFile().getAbsoluteFile();
return true;
}
}
/**
* Navigiert das aktuelle Verzeichnis in den angegebenen Pfad relativ
* zum aktuellen Verzeichnis. Ist der neue Pfad ein existierender Pfad
* innerhalb des Wurzelverzeichnisses, dann wird das aktuelle Verzeichnis
* gewechselt, sonst nicht.
*
* @param pPath
* @return <code>true</code> - Verzeichnis geändert, <code>false</code> - Navigation nicht möglich
*/
public boolean cd( String pPath ) {
File newDir = new File(currentDir, pPath);
if( newDir.exists() && newDir.isDirectory()
&& newDir.getAbsolutePath().startsWith(root.getAbsolutePath()) ) {
currentDir = newDir;
return true;
} else {
return false;
}
}
/**
* Erstellt eine leere Datei am angegebene Ort. Gibt es schon eine Datei mit
* dem Namen oder existieren keine Schreibrechte passiert nichts.
*
* @param pName
* @return <code>true</code> - Datei erzeugt, <code>false</code> - Aktion nicht möglich
*/
public boolean createFile( String pName ) {
return createFile(pName, "");
}
public boolean createFile( String pName, String content ) {
File newFile = new File(currentDir, pName);
try {
newFile.getParentFile().mkdirs();
newFile.createNewFile();
} catch( Exception e ) {
return false;
}
if( content.length() > 0 ) {
try {
BufferedWriter writer = new BufferedWriter(new FileWriter(newFile));
writer.write(content);
writer.flush();
} catch( IOException e ) {
return false;
}
}
return true;
}
/**
* Erstellt ein neues Verzeichnis mit dem angegebenen Namen relativ
* zum aktuellen Verzeichnis.
*
* @param pName
* @return <code>true</code> - Verzeichnis erzeugt, <code>false</code> - Aktion nicht möglich
*/
public boolean createDirectory( String pName ) {
File newFile = new File(currentDir, pName);
try {
newFile.mkdirs();
} catch( Exception e ) {
return false;
}
return true;
}
}

284
ID3.java Normal file
View File

@ -0,0 +1,284 @@
/**
* Berechnung der Kenngrößen für den ID3-Algorithmus. Nachdem alle
* Datensätze gezählten wurden können Entropie und Informationsgewinn
* der Attribute und des Gesamtdatensatzes abgerufen werden.
*/
public class ID3 {
private int total_0 = 0, total_1 = 0;
private java.util.HashMap<String, Integer> counts;
public ID3() {
counts = new java.util.HashMap<String, Integer>();
}
/**
* Zählt die Attribute des übergebenen Passagiers.
*
* @param pPassenger Ein Passagier-Datensatz
*/
public void count( Passenger pPassenger ) {
count(pPassenger.survived);
count("clazz", pPassenger.clazz, pPassenger.survived);
count("sex", pPassenger.sex, pPassenger.survived);
count("age", pPassenger.age, pPassenger.survived);
count("sibsp", pPassenger.sibsp, pPassenger.survived);
count("parch", pPassenger.parch, pPassenger.survived);
count("embarked", pPassenger.embarked, pPassenger.survived);
}
/**
* Erhöht den Zähler für Überlebende / Verstobene Passagiere im
* gesamten Datensatz.
*
* @param pSurvived 1 - überlebt, 0 - verstorben
*/
public void count( int pSurvived ) {
if( pSurvived == 0 ) {
total_0 += 1;
} else {
total_1 += 1;
}
}
/**
* Erhöht den Zähler für die Kombination aus Attribut, Wert
* und Zielwert um Eins.
*
* @param pAttribute Name des Attributs
* @param pValue Konkreter Wert des Attributs
* @param pSurvived 1 - überlebt, 0 - verstorben
*/
public void count( String pAttribute, int pValue, int pSurvived ) {
count(pAttribute, String.valueOf(pValue), pSurvived);
}
/**
* Erhöht den Zähler für die Kombination aus Attribut, Wert
* und Zielwert um Eins.
*
* @param pAttribute Name des Attributs
* @param pValue Konkreter Wert des Attributs
* @param pSurvived 1 - überlebt, 0 - verstorben
*/
public void count( String pAttribute, String pValue, int pSurvived ) {
String name = getKey(pAttribute, pValue, pSurvived);
counts.put(name, counts.getOrDefault(name, 0) + 1);
}
/**
* @return Anzahl an bisher gezählten Datensätzen.
*/
public int getTotal() {
return total_0 + total_1;
}
/**
* Ermittelt die Anzahl an bisher gezählten Datensätzen, die den
* angegebenen Zielwert haben.
*
* @param pSurvived 1 - überlebt, 0 - verstorben
* @return Anzahl an passenden Datensätzen.
*/
public int getTotal( int pSurvived ) {
if( pSurvived == 0 ) {
return total_0;
} else {
return total_1;
}
}
/**
* Anzahl bisher gezählter Datensätze mit der angegebenen
* Attribut/Wert Kombination.
*
* @param pAttribute Name des Attributs
* @param pValue Konkreter Wert des Attributs
* @return
*/
public int getCount( String pAttribute, int pValue ) {
return getCount(pAttribute, String.valueOf(pValue));
}
/**
* Anzahl bisher gezählter Datensätze mit der angegebenen
* Attribut/Wert Kombination.
*
* @param pAttribute Name des Attributs
* @param pValue Konkreter Wert des Attributs
* @return
*/
public int getCount( String pAttribute, String pValue ) {
int anzahl_0 = getCount(pAttribute, pValue, 0);
int anzahl_1 = getCount(pAttribute, pValue, 1);
return anzahl_0 + anzahl_1;
}
/**
* Anzahl bisher gezählter Datensätze mit der angegebenen
* Attribut/Wert/Zielwert Kombination.
*
* @param pAttribute Name des Attributs
* @param pValue Konkreter Wert des Attributs
* @param pSurvived 1 - überlebt, 0 - verstorben
* @return
*/
public int getCount( String pAttribute, int pValue, int pSurvived ) {
return getCount(pAttribute, String.valueOf(pValue), pSurvived);
}
/**
* Anzahl bisher gezählter Datensätze mit der angegebenen
* Attribut/Wert/Zielwert Kombination.
*
* @param pAttribute Name des Attributs
* @param pValue Konkreter Wert des Attributs
* @param pSurvived 1 - überlebt, 0 - verstorben
* @return
*/
public int getCount( String pAttribute, String pValue, int pSurvived ) {
String name = getKey(pAttribute, pValue, pSurvived);
return counts.getOrDefault(name, 0);
}
/**
* Anteil des Zielwertes am Gesamtdatensatz.
*
* @param pSurvived 1 - überlebt, 0 - verstorben
* @return
*/
public double getRatio( int pSurvived ) {
return (double) getTotal(pSurvived) / (double) getTotal();
}
/**
* Anteil der Datensätze mit der übergebenen Attribut/Wert
* Kombination am Gesamtdatensatz.
*
* @param pAttribute Name des Attributs
* @param pValue Konkreter Wert des Attributs
* @return
*/
public double getRatio( String pAttribute, int pValue ) {
return getRatio(pAttribute, String.valueOf(pValue));
}
/**
* Anteil der Datensätze mit der übergebenen Attribut/Wert
* Kombination am Gesamtdatensatz.
*
* @param pAttribute Name des Attributs
* @param pValue Konkreter Wert des Attributs
* @return
*/
public double getRatio( String pAttribute, String pValue ) {
return (double) getCount(pAttribute, pValue) / (double) getTotal();
}
/**
* Anteil der Datensätze mit der übergebenen Attribut/Wert/Zielwert
* Kombination am Gesamtdatensatz.
*
* @param pAttribute Name des Attributs
* @param pValue Konkreter Wert des Attributs
* @return
*/
public double getRatio( String pAttribute, int pValue, int pSurvived ) {
return getRatio(pAttribute, String.valueOf(pValue), pSurvived);
}
/**
* Anteil der Datensätze mit der übergebenen Attribut/Wert/Zielwert
* Kombination am Gesamtdatensatz.
*
* @param pAttribute Name des Attributs
* @param pValue Konkreter Wert des Attributs
* @return
*/
public double getRatio( String pAttribute, String pValue, int pSurvived ) {
return (double) getCount(pAttribute, pValue, pSurvived) / (double) getCount(pAttribute, pValue);
}
/**
* Entropie des Gesamtdatensatzes.
*
* @return
*/
public double entropie() {
double ratio_0 = getRatio(0);
double ratio_1 = getRatio(1);
return -1 * ratio_0 * log2(ratio_0) - ratio_1 * log2(ratio_1);
}
/**
* Entropie der Attribut/Wert Kombination.
*
* @param pAttribute Name des Attributs
* @param pValue Konkreter Wert des Attributs
* @return
*/
public double entropie( String pAttribute, int pValue ) {
return entropie(pAttribute, String.valueOf(pValue));
}
/**
* Entropie der Attribut/Wert Kombination.
*
* @param pAttribute Name des Attributs
* @param pValue Konkreter Wert des Attributs
* @return
*/
public double entropie( String pAttribute, String pValue ) {
double ratio_0 = getRatio(pAttribute, pValue, 0);
double ratio_1 = getRatio(pAttribute, pValue, 1);
return -1 * ratio_0 * log2(ratio_0) - ratio_1 * log2(ratio_1);
}
/**
* Informationsgewinn eines Attributs.
*
* @param pAttribute
* @return
*/
public double informationgain( String pAttribute ) {
double ig = entropie();
String[] values = Passenger.getValues(pAttribute);
for( int j = 0; j < values.length; j++ ) {
String value = values[j];
if( getCount(pAttribute, value) > 0 ) {
double a = getRatio(pAttribute, value);
double e = entropie(pAttribute, value);
ig -= (a * e);
}
}
return ig;
}
/**
* Hilfsfunktion zur Berechnung des Logarithmus zu Basis 2.
*
* @param x
* @return
*/
private double log2( double x ) {
return Math.log10(x) / Math.log10(2);
}
/**
* Hilfsfunktion um aus einer Attribut/Wert/Zielwert Kombination
* einen Hash-Key zu genereieren.
*
* @param attribut
* @param wert
* @param ziel
* @return
*/
private String getKey( String attribut, String wert, int ziel ) {
return attribut + ":" + wert + ":" + ziel;
}
}

345
List.java Normal file
View File

@ -0,0 +1,345 @@
/**
* <p>
* Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
* </p>
* <p>
* Generische Klasse List<ContentType>
* </p>
* <p>
* Objekt der generischen Klasse List verwalten beliebig viele linear
* angeordnete Objekte vom Typ ContentType. Auf hoechstens ein Listenobjekt,
* aktuellesObjekt genannt, kann jeweils zugegriffen werden.<br />
* Wenn eine Liste leer ist, vollstaendig durchlaufen wurde oder das aktuelle
* Objekt am Ende der Liste geloescht wurde, gibt es kein aktuelles Objekt.<br />
* Das erste oder das letzte Objekt einer Liste koennen durch einen Auftrag zum
* aktuellen Objekt gemacht werden. Ausserdem kann das dem aktuellen Objekt
* folgende Listenobjekt zum neuen aktuellen Objekt werden. <br />
* Das aktuelle Objekt kann gelesen, veraendert oder geloescht werden. Ausserdem
* kann vor dem aktuellen Objekt ein Listenobjekt eingefuegt werden.
* </p>
*
* @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
* @version Generisch_06 2015-10-25
*/
public class List<ContentType> {
/* --------- Anfang der privaten inneren Klasse -------------- */
private class ListNode {
private ContentType contentObject;
private ListNode next;
/**
* Ein neues Objekt wird erschaffen. Der Verweis ist leer.
*
* @param pContent das Inhaltsobjekt vom Typ ContentType
*/
private ListNode(ContentType pContent) {
contentObject = pContent;
next = null;
}
/**
* Der Inhalt des Knotens wird zurueckgeliefert.
*
* @return das Inhaltsobjekt des Knotens
*/
public ContentType getContentObject() {
return contentObject;
}
/**
* Der Inhalt dieses Kontens wird gesetzt.
*
* @param pContent das Inhaltsobjekt vom Typ ContentType
*/
public void setContentObject(ContentType pContent) {
contentObject = pContent;
}
/**
* Der Nachfolgeknoten wird zurueckgeliefert.
*
* @return das Objekt, auf das der aktuelle Verweis zeigt
*/
public ListNode getNextNode() {
return this.next;
}
/**
* Der Verweis wird auf das Objekt, das als Parameter uebergeben
* wird, gesetzt.
*
* @param pNext der Nachfolger des Knotens
*/
public void setNextNode(ListNode pNext) {
this.next = pNext;
}
}
/* ----------- Ende der privaten inneren Klasse -------------- */
// erstes Element der Liste
ListNode first;
// letztes Element der Liste
ListNode last;
// aktuelles Element der Liste
ListNode current;
/**
* Eine leere Liste wird erzeugt.
*/
public List() {
first = null;
last = null;
current = null;
}
/**
* Die Anfrage liefert den Wert true, wenn die Liste keine Objekte enthaelt,
* sonst liefert sie den Wert false.
*
* @return true, wenn die Liste leer ist, sonst false
*/