Mit org.datavec.api.records.reader.impl.csv.CSVNLinesSequenceRecordReader einfache CSV mit double-Werten einlesen

Ich bin jetzt schon zwei Stunden am Rätselraten und komme nicht weiter. Vorweg: Meine Locale ist nicht auf en_us eingestellt.

Ich hab eine einfache CSV-Datei mit 500 Zeilen und jeweils 5 double-Werten, und zwar so:
1.0;2.1;3.000000;4.56;5.0
usw.

Das ; ist also das Trennzeichen und "\n" oder "\r\n" die neue Zeile.

Ausschnitt Code:

CSVNLinesSequenceRecordReader reader = new CSVNLinesSequenceRecordReader(5, 0, ";");
reader.initialize(new NumberedFileInputSplit("C:\\...\\Data\\" + name + "_%d.csv", 0, 499));
SequenceRecordReaderDataSetIterator iterator = new SequenceRecordReaderDataSetIterator(reader, batchSize, numLabelClasses, 1);

Importe:

import org.datavec.api.conf.Configuration;
import org.datavec.api.records.reader.impl.csv.CSVNLinesSequenceRecordReader;
import org.datavec.api.records.reader.impl.csv.CSVSequenceRecordReader;
import org.datavec.api.split.FileSplit;
import org.datavec.api.split.NumberedFileInputSplit;
import org.deeplearning4j.datasets.datavec.SequenceRecordReaderDataSetIterator;
//...

Fehlermeldung:

Exception in thread "ADSI prefetch thread" java.lang.RuntimeException: java.lang.NumberFormatException: For input string: "39829.78;39859.86;39682.6;39754.42;794.958726"
	at org.nd4j.linalg.dataset.AsyncDataSetIterator$AsyncPrefetchThread.run(AsyncDataSetIterator.java:437)
Caused by: java.lang.NumberFormatException: For input string: "39829.78;39859.86;39682.6;39754.42;794.958726"
	at java.base/jdk.internal.math.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2054)
	at java.base/jdk.internal.math.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
	at java.base/java.lang.Double.parseDouble(Double.java:549)
	at org.datavec.api.writable.Text.toDouble(Text.java:595)
	at org.deeplearning4j.datasets.datavec.RecordReaderMultiDataSetIterator.convertWritablesSequence(RecordReaderMultiDataSetIterator.java:710)
	at org.deeplearning4j.datasets.datavec.RecordReaderMultiDataSetIterator.convertFeaturesOrLabels(RecordReaderMultiDataSetIterator.java:369)
	at org.deeplearning4j.datasets.datavec.RecordReaderMultiDataSetIterator.nextMultiDataSet(RecordReaderMultiDataSetIterator.java:327)
	at org.deeplearning4j.datasets.datavec.RecordReaderMultiDataSetIterator.next(RecordReaderMultiDataSetIterator.java:213)
	at org.deeplearning4j.datasets.datavec.SequenceRecordReaderDataSetIterator.next(SequenceRecordReaderDataSetIterator.java:365)
	at org.deeplearning4j.datasets.datavec.SequenceRecordReaderDataSetIterator.next(SequenceRecordReaderDataSetIterator.java:344)
	at org.deeplearning4j.datasets.datavec.SequenceRecordReaderDataSetIterator.next(SequenceRecordReaderDataSetIterator.java:48)
	at org.nd4j.linalg.dataset.AsyncDataSetIterator$AsyncPrefetchThread.run(AsyncDataSetIterator.java:411)

Doku:
https://deeplearning4j.org/api/latest/org/datavec/api/records/reader/impl/csv/package-summary.html

Ausschnitt aus der Doku:

A CSV Sequence record reader where:
(a) all time series are in a single file
(b) each time series is of the same length (specified in constructor)
(c) no delimiter is used between time series
For example, with nLinesPerSequence=10, lines 0 to 9 are the first time series, 10 to 19 are the second, and so on.

Siehe auch:
https://deeplearning4j.konduit.ai/getting-started/tutorials/recurrent-networks#iterating-from-disk (Abschnitt „Iterating from disk“)

Sieht jemand vielleicht auf Anhieb, wie man die Daten da am einfachsten hereinbekommen kann?

Tja, dann erwartet das Ding die Zahlen wohl mit Kommas statt mit Punkten. Kannst ja mal spaßeshalber ausprobieren, ob das Einlesen klappt, wenn du die Punkte durch Kommas ersetzt.

Nah, da steht schon Double.parseDouble, also eigentlich sollte das passen. Was mich irritiert ist der „input string“ "39829.78;39859.86;39682.6;39754.42;794.958726", der ja offenbar nicht getokenized ist. Hab’ den entsprechenden Code aber nur überflogen. Locale auf Englisch stellen oder Punkte durch Kommas ersetzen wären zwar rumprobier-Optionen, aber ansonsten würd’ ich da halt mal den Debugger anwerfen. Kann ja nicht so schwer sein.

Ja hab ich schon probiert, dann wird auch richtigerweise an Double.parseDouble delegiert, allerdings fliegt dort dann die Exception wegen des ,

Es kann doch nicht so schwer sein, eine einfach CSV einzulesen… Ich versuche es jetzt noch mal.

Sollte man meinen. CSVNLinesSequenceRecordReader (~200 Zeilen) extends CSVRecordReader (ca. 200 Zeilen) extends LineRecordReader (ca. 350 Zeilen) extends BaseRecordReader (ca. 100 Zeilen), und die Exception fliegt in einem RecordReaderMultiDataSetIterator (ca. 1000 Zeilen) wo auch noch ein SequenceRecordReaderDataSetIterator (ca. 400 Zeilen) eine Rolle spielt. Was auch immer die da gemacht haben: CSVs lesen sollte einfacher sein.

(Ja, es ist viel komplizierter, als man zuerst meint, aber es sollte deutlich einfacher sein, als das. Als ich bei meiner „data-lib“ CSV-support einbauen wollte, habe ich auch erst was handegklöppeltes verwendet. Wahnsinnig effizient. Und wahnsinnig wenig durchdacht. Dann habe ich die CSV-Parser von „Jackson“, „Apache“ und „Simple Flat Mapper“ durchprobiert, und bin bei letzterem vorerst geblieben. Nützt dir jetzt aber halt alles nix :smiley: )

Ich dachte mir, ich könnte ihn mit einem simplen Workaround überlisten:

String name = string + pair;

ArrayList<ArrayList<Double>> data1 = new ArrayList<>();
ArrayList<ArrayList<Double>> data2 = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader("C:\\Users\\tobia\\Desktop\\BNData\\" + name + "_0.csv"))) {
	String l;
	while ((l = br.readLine()) != null) {
		ArrayList<Double> temp = new ArrayList<>();
		String[] s = l.split(";");
		for (int i = 0; i < s.length; i++) {
			temp.add(Double.parseDouble(s[i]));
		}
		data1.add(temp);
	}
}
try (BufferedReader br = new BufferedReader(new FileReader("C:\\Users\\tobia\\Desktop\\BNData\\" + name + "_1.csv"))) {
	String l;
	while ((l = br.readLine()) != null) {
		ArrayList<Double> temp = new ArrayList<>();
		String[] s = l.split(";");
		for (int i = 0; i < s.length; i++) {
			temp.add(Double.parseDouble(s[i]));
		}
		data2.add(temp);
	}
}

int batchSize = 128;
int numLabelClasses = 5;

CSVNLinesSequenceRecordReader reader = new CSVNLinesSequenceRecordReader(5, 0, ";") {
	private static final long serialVersionUID = 1L;
	int index = 0;

	@Override
	public boolean hasNext() {
		return index < data1.size();
	}

	@Override
	public List<Writable> next() {
		return data1.get(index++).stream().map(d -> new DoubleWritable(d)).collect(Collectors.toList());
	}
};
// reader.initialize(new FileSplit(new File("C:\\Users\\tobia\\Desktop\\BNData")));
SequenceRecordReaderDataSetIterator iterator = new SequenceRecordReaderDataSetIterator(reader, batchSize, numLabelClasses, 1);

Aber dann sagt er mir, Nene, Freundchen, du hast den CSVNLinesSequenceRecordReader noch nicht initialisiert… Wenn ich versuche, ihn mit null oder einem Dummy-FileSplit zu initialisieren, bricht er ebenfalls sofort ab.


Das ist das Problem, und ich sehe keinen darin, warum es so kompliziert gemacht wurde. :frowning:
Vielleicht soll man das „Framework“ gar nicht benutzen können. :smile: