forked from IF-LK-2020/id3
Initial commit
This commit is contained in:
commit
b526997439
|
@ -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*
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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 + "]";
|
||||
}
|
||||
|
||||
}
|
|
@ -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 + "]";
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
*/
|
||||