Seite 1 von 1

File schnell 2byte-weise einlesen

Verfasst: Dienstag 30. Mai 2006, 14:09
von tromai
Hallo zusammen,

ich habe eine Frage zum Einlesen von Dateien.

Das Problem ist das folgende:
Ich habe eine recht große Datei, die ich in ein 2d NumPy-Array einlesen möchte. Und zwar sollen immer 2 Zeichen in ein Array-Element geladen werden.

Bisher habe ich das folgendermaßen gemacht:

Code: Alles auswählen

File = file('Filename')
for n in range(900):
	for m in range(900):
		array[n][m] = File.read(2)
Das klappt wunderbar. Das Problem ist, dass das einfach zu lange für die Anwendung dauert. Kann mir jemand eine schnellere Methode empfehlen das File einzulesen?

Danke schonmal im voraus.

Re: File schnell 2byte-weise einlesen

Verfasst: Dienstag 30. Mai 2006, 16:06
von gerold
tromai hat geschrieben:Bisher habe ich das folgendermaßen gemacht:

Code: Alles auswählen

File = file('Filename')
for n in range(900):
	for m in range(900):
		array[n][m] = File.read(2)
Hi tromai!

Vielleicht ist das schneller:

Code: Alles auswählen

daten = []

f = file('hallo.bin', "rb")
try:
    for paket in iter(lambda: f.read(1024), ""):
        zaehler = 0
        for zaehler in range(0, len(paket), 2):
            try:
                daten.append(
                    (paket[zaehler], paket[zaehler + 1])
                )
            except:
                print "ACHTUNG!!! Anzahl Zeichen -- ungerade!"
finally:    
    f.close()

print daten
Aber nur vielleicht...

Du musst es natürlich auf dein Array umsetzen. Ich habe noch nie mit Python-Arrays gearbeitet, deshalb habe ich dieses Beispiel einfach als Liste mit Tuppeln umgesetzt.

mfg
Gerold
:-)

Re: File schnell 2byte-weise einlesen

Verfasst: Mittwoch 31. Mai 2006, 07:45
von BlackJack
tromai hat geschrieben:Das Problem ist das folgende:
Ich habe eine recht große Datei, die ich in ein 2d NumPy-Array einlesen möchte. Und zwar sollen immer 2 Zeichen in ein Array-Element geladen werden.
Wieso Zeichen in einem NumPy-Array? Die sind doch eher für Zahlen gedacht. Sicher das Du nicht 16-Bit Zahlen da drin haben möchtest?
Bisher habe ich das folgendermaßen gemacht:

Code: Alles auswählen

File = file('Filename')
for n in range(900):
	for m in range(900):
		array[n][m] = File.read(2)
Das klappt wunderbar. Das Problem ist, dass das einfach zu lange für die Anwendung dauert. Kann mir jemand eine schnellere Methode empfehlen das File einzulesen?
Funktioniert vielleicht etwas in der Richtung:

Code: Alles auswählen

import numpy
array = numpy.fromfile('Filename', dtype='<i2', count=900 * 900)
array = array.reshape(900, 900)

Verfasst: Mittwoch 31. Mai 2006, 09:17
von CM
Hoi,

würde mich wundern, wenn das funktioniert:
1.) count wird auf 810000 gesetzt, was bedeutet, das ebensoviele Zeichen vom gewünschten Typ eingelesen werden. Das muß nicht schlimm sein, limitiert aber künstlich. Besser ist wahrscheinlich der Default von -1 = das ganze File.
2.) wenn "array" ein numpy-array ist, ist ein Variablenname "array" ungünstig.

Davon abgesehen finde ich den Vorschlag aber gut.

Im Übrigen tendiere ich dazu Daten in Listen einzulesen und für den weiteren Gebrauch in arrays umzuwandeln - das ist zwar aufwendiger, aber meist übersichtlicher (finde ich). Der Overhead ist meist vernachlässigbar. Speichern mache ich dann über picklen wie im link beschrieben.

Gruß,
Christian

PS
Sollten die 2 Byte mit einem 16bit Bild korrespondieren gibt es die Möglichkeit in Kombination mit scipy.misc.fromimage zu konvertieren.

Verfasst: Mittwoch 31. Mai 2006, 15:12
von tromai
Ok, auf jeden Fall mal danke.

Ich werde mal die unterschiedlichen Varianten ausprobieren und dann berichten. Kann allerdings ein paar Tage dauern.

Verfasst: Donnerstag 1. Juni 2006, 09:36
von tromai
So, jetzt habe ich doch noch einmal ein paar Fragen:

1. Habe ich das richtig verstanden, dass dtype='<i2' die Anzahl der Zeichen festlegt?
2. Ich habe bisher keine gut Docu zu dem Befehl numpy.fromfile gefunden. Vielleicht kennt ja jemand einen Link.
3. Gibt es eine Möglichkeit einen Offset anzugeben? Für das File um das es ursprünglich wäre das nicht nötig. Allerdings ist nun auch noch ein zweites File mit einem Header dazugekommen.

Danke schonmal im Voraus.

[Edit: Frage 1 und 3 konnte ich mir Mittlerweile selbst beantworten. Allerdings ist jetzt noch eine Frage 4 dazu gekommen:
Un zwar erhalte, ich wenn ich ein Textfile mir dem Inhalt mir dem Befehl:

Code: Alles auswählen

arrh = numpy.fromfile(radFile, dtype = '<i2', count = 2)
einlese, die Werte

Code: Alles auswählen

[12849 13363]
Ok, wie das zu Stande kommt ist klar. Der erste Wert berechnet sich folgendermaßen:
ASCII-Code für 1 ist 49
ASCII-Code für 2 ist 50
49+50*256=12849

Es wäre also kein Problem das ganze zurückzurechnen. Allerdings würde das wiederum das schnelle Einlesen zunichte machen. Gibt es eine Funktion, die mir das direkt für einen kompletten array umwandelt? Bzw. kann ich das ganze auch gleich als Zahl oder 1 zu 1 als String einlesen, so dass ich dann in meinem Array 12 und 34 stehen habe?

Verfasst: Donnerstag 1. Juni 2006, 12:57
von CM
Hoi,

1.) Nein, hast Du nicht: dtype legt den "minimalen" Datentyp Deines Arrays fest.
Beispiel:

Code: Alles auswählen

a = arange(10,dtype='<i2') #array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=int16)
b = arange(10) #array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
c = arange(10,dtype='i2') #array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=int16)
Eine Erklärung magst Du hier finden.
2.) Einen Link kenne ich nicht, aber ein Beispiel.
Und den Docstring:

Code: Alles auswählen

>>> print numpy.fromfile.__doc__
fromfile(file=, dtype=int, count=-1, sep='')

  Return an array of the given data type from a
  (text or binary) file.   The file argument can be an open file
  or a string with the name of a file to read from.  If
  count==-1, then the entire file is read, otherwise count is
  the number of items of the given type read in.  If sep is ''
  then read a binary file, otherwise it gives the separator
  between elements in a text file.

  WARNING: This function should be used sparingly, as it is not
  a platform-independent method of persistence.  But it can be
  useful to read in simply-formatted or binary data quickly.
3.) Nicht implizit. Aber Du kannst Deine eigene Funktion schreiben, die den Header überspring. (fromfile akzeptiert als input ja nicht nur einen Dateinamen, sondern auch das geöffnete File.)

Außerdem: Kennst Du PyTables. Das baucht zwar z. Zt. noch auf numarray auf, wird aber in absehbarer Zeit auf numpy umstellen und die Konvertierung von numarray zu numpy arrays ist ja leicht.

Funktioniert denn jetzt das Einlesen Deines ursprünglichen Files?

Gruß,
Christian

Verfasst: Donnerstag 1. Juni 2006, 13:19
von tromai
*g*
Da haben wir wohl gleichzeitig geschrieben. Frage 1 war dann doch noch nicht geklärt. Aber jetzt habe ich es verstanden.

THX

Verfasst: Donnerstag 1. Juni 2006, 13:26
von tromai
Ok, die letzte offene Frage hat sich auch geklärt über dtype='S2' lässt sich ein string der länge 2 einlesen.

Verfasst: Donnerstag 8. Juni 2006, 09:28
von tromai
Jetzt ist doch noch ein Problem aufgetreten.
Ich lese das File über diesen Befehl ein

Code: Alles auswählen

arrh = numpy.fromfile(radFile, dtype = 'S2', count = 900*900)
Mein Problem ist jetzt, dass das File gezippt ist.
Über gzip.open lässt sich das File ja ohne Probleme öffnen:

Code: Alles auswählen

radFile = gzip.open('blabla.gz')
Allerdings bekomme ich da bei dem Befehl "numpy.fromfile" Probleme. Das Script sieht folgendermaßen aus:

Code: Alles auswählen

radFile = gzip.open('blabla.gz')
readHeader(radFile)
arrh = numpy.fromfile(radFile, dtype = 'S2', count = 900*900)
Und gibt folgenden Fehler aus:

Code: Alles auswählen

Traceback (most recent call last):
  File "funcV5.py", line 87, in radIn
    arrh = numpy.fromfile(radFile, dtype = 'S2', count = 900*900)
IOError: first argument must be an open file
Allerdings hatte ich gzip.open so verstanden, dass es das File als Objekt importiert und somit sollte es doch keinen derartigen Fehler geben. Aber scheinbar liege ich da falsch :P

Vielleicht hat ja jemand eine Lösung für das Problem.

Verfasst: Freitag 9. Juni 2006, 06:45
von BlackJack
Frag am besten auf der numpy-Mailingliste nach, warum die das nicht als Datei-Objekt akzeptieren.

Verfasst: Montag 12. Juni 2006, 08:55
von tromai
Ok, werde ich mal machen. Ich habe mittlerweile von anderer Stelle Support bekommen und es folgendermaßen gelöst:

Code: Alles auswählen

gzstream = open('file.gz', 'rb')
gz = GzipFile(fileobj=gzstream, mode='rb')
streamBuf = StringIO()
streamBuf.write(gz.read())
arrh = numpy.fromstring(streamBuf.getvalue(), dtype = 'S2', count = 900*900) 
Ich habe auch gleich die nächste Frage:
Ich verwende ja dtype='S2'.
Welchen dtype müsste mann denn verwenden, wenn man einfach 2 Byte einlesen will? Das Problem ist, dass in dem File des öfteren der Wert NULL vorkommt und in dem String dann natürlich nichts drin steht. Wie kann ich das Problem umgehen? Wenn ich das File mit read(2) auslese, dann gibt es keine Probleme.

Vielleicht weiß ja auch jemand wo ich eine Liste aller dtypes herbekomme.

Verfasst: Montag 12. Juni 2006, 22:33
von BlackJack
tromai hat geschrieben:Ok, werde ich mal machen. Ich habe mittlerweile von anderer Stelle Support bekommen und es folgendermaßen gelöst:

Code: Alles auswählen

gzstream = open('file.gz', 'rb')
gz = GzipFile(fileobj=gzstream, mode='rb')
streamBuf = StringIO()
streamBuf.write(gz.read())
arrh = numpy.fromstring(streamBuf.getvalue(), dtype = 'S2', count = 900*900) 
Das kannst Du auch einfacher haben. `gz.read()` liefert schon eine Zeichenkette, die muss man nicht nochmal in ein StringIO Objekt verpacken und dann wieder rausholen.

Code: Alles auswählen

gzstream = open('file.gz', 'rb')
gz = GzipFile(fileobj=gzstream, mode='rb')
arrh = numpy.fromstring(gz.read(), dtype = 'S2', count = 900*900)
Ich habe auch gleich die nächste Frage:
Ich verwende ja dtype='S2'.
Welchen dtype müsste mann denn verwenden, wenn man einfach 2 Byte einlesen will? Das Problem ist, dass in dem File des öfteren der Wert NULL vorkommt und in dem String dann natürlich nichts drin steht. Wie kann ich das Problem umgehen? Wenn ich das File mit read(2) auslese, dann gibt es keine Probleme.

Warum möchtest Du überhaupt Zeichenketten in einem Numpy-Array haben? Was machst Du mit den Daten nachdem sie eingelesen wurden?

Verfasst: Dienstag 13. Juni 2006, 12:52
von tromai

Code: Alles auswählen

streamBuf = StringIO()
streamBuf.write(gz.read())
Der Teil des Codes stammt nicht von mir. Was genau das Problem war, wenn man nur gz.read() gemacht hat kann ich dir nicht sagen. Auf jeden Fall gab es dann Probleme mir fromstring(). So geht es jetzt. Aber ich werde nochmal ausprobieren.

Das Einlesen als string stammte noch aus aus einem früheren Entwicklungsstadium. Ist aber natürlich völlig überflüssig. Danke für den Hinweis. Das hat nämlich jetzt mein Problem komplett gelöst. Hätte ich auch früher drauf kommen können :oops: :P

Code: Alles auswählen

gz = GzipFile(fileobj=gzstream, mode='rb')
streamBuf = StringIO()
streamBuf.write(gz.read())
radFile = streamBuf.getvalue()
arrh = numpy.fromstring(radFile, dtype = numpy.uint16, count = 900*900)