Vor einigen Jahren habe ich ein Python-Skript geschrieben, um FCS-Dateien in ein durch Tabulatoren getrenntes Format zu konvertieren. Es war weit davon entfernt, alle Möglichkeiten zu nutzen, die die Formatbeschreibung bietet, aber es funktionierte zumindest für einige der Dateien, die auf einem unserer Computer erstellt wurden: http://www.igh.cnrs.fr/equip/Seitz/ en_equipe-programmes.html
Die Formatdokumentation, die ich gefunden habe, hat die Dekodierung aktiviert (siehe Abschnitt 3 des von Ihnen erwähnten PDFs), erfordert jedoch das Lesen von Daten im Binärmodus.
Die allgemeine Idee dieses Formats (und ich denke, viele andere Binärformate) ist, dass es am Anfang der Datei eine Headerzone mit einer definierten Anzahl von Feldern gibt, die Zahlen codieren, die angeben, wie der Rest der Datei strukturiert ist. Eine erste Phase besteht darin, diesen Header gemäß der Beschreibung in der Dokumentation des Formats zu analysieren. Die aus dem Header extrahierten Informationen geben an, wo die Daten zu finden sind und wie sie codiert werden, und zwar gemäß den in der Formatdokumentation beschriebenen Regeln.
Falls dies nützlich sein kann und für den Datensatz, hier ist die Code aus dem oben genannten Skript (nachdem Kommentare entfernt wurden, von denen einige lediglich aus der Formatdokumentation kopiert wurden, und einige hinzugefügt wurden):
#! / usr / bin / env python "" "Dieses Skript versucht, FCS-Durchflusszytometriedaten zu lesen. Formatanalyse, inspiriert von Informationen, die hier zu finden sind: http: //isac-net.org/Resources-for-Cytometrists/Data-Standards/ Data-File-Standards / Durchflusszytometrie-Data-File-Format-Standards.aspx "" import re # So dekodieren Sie binär codierte Datenimport-Strukturimport-Systemklasse Parameter (Objekt): "" Dieses Objekt repräsentiert einen der Parametertypen, die sind in einem DATA-Segment einer FCS-Datei vorhanden. "" __slots__ = ("p_name", "p_bits", "p_range", "p_ampl", "parser") def __init __ (self, p_name, p_bits, p_range, p_am pl): self.p_name = p_name self.p_bits = p_bits self.p_range = p_range self.p_ampl = p_ampl
# Funktion zum Parsen eines Parameters im Datensegment self.parser = Keine ################################## ################ Hier beginnt das Parsen des Header-Teils ##, der angibt, wo sich die anderen Teile befinden. ################################################# f = offen (sys.argv [1], "rb") # Der Formatname wird in 6 Buchstaben codiert. # Ein ASCII-Buchstabe wird mit einem octetfile_format = "" .join ([f.read (1) für __ in range (6)) codiert. ]) sys.stdout.write ("Format:% s \ n"% file_format) # Die Formatbeschreibungen reservieren 4 Oktette, die wir überspringen = f.read (4) # 8 Oktettblöcke codieren die Start- und Endpositionen # verschiedener Teile des datatext_start = int (f.read (8) .strip ("")) text_end = int (f.read (8) .strip ("")) data_start = int (f.read (8) .strip (" ")) data_end = int (f.read (8) .strip (" ")) analysis_start = int (f.read (8) .strip (" ")) analysis_end = int (f.read (8) .strip ( "")) if (analysis_start und analysis_end): sys.stderr.write ("Kann das ANALYSIS-Segment einer FCS-Datei nicht verarbeiten. \ n") ################### ##################################### Hier beginnt das Parsen des "TEXT" -Teils ##, der beschreibt, wie Die eigentlichen Daten sind organisiert ################################################ ######## f.seek (text_start) # Das erste Zeichen im primären TEXT-Segment ist das ASCII-Trennzeichen Zeichen.sep = f.read (1), wenn sep nicht in ["_", "@"]: alt_sep = "_ @ _" elif sep nicht in ["_", " | "]: alt_sep =" _ | _ "else: sep nicht in [" + "," | "] bestätigen alt_sep =" + | + "text_segment = f.read (text_end - text_start) fields = text_segment.split (sep ) info = {} i = 0, während i < len (Felder) - 1: Schlüssel = Felder [i] i + = 1 Wert = Felder [i] i + = 1 # Bei Schlüsselwörtern wird die Groß- und Kleinschreibung nicht berücksichtigt. Sie können in eine Datei geschrieben werden in Kleinbuchstaben, Großbuchstaben oder einer # Mischung aus beiden. Ein FCS-Dateireader muss jedoch die Groß- und Kleinschreibung ignorieren. Ein Schlüsselwortwert kann # in Kleinbuchstaben, Großbuchstaben oder eine Mischung aus beiden sein. Bei Schlüsselwortwerten wird zwischen Groß- und Kleinschreibung unterschieden. info [key.upper ()] = val
print "% s Ereignisse wurden erkannt." % info ["$ TOT"] print "Jedes Ereignis ist durch% s Parameter gekennzeichnet."% info ["$ PAR"] if info ["$ NEXTDATA"]! = "0": sys.stderr.write ("Some other" Daten sind in der Datei vorhanden, wurden jedoch nicht analysiert. \ n ") # L - Listenmodus. Für jedes Ereignis wird der Wert jedes Parameters in der Reihenfolge gespeichert, in der die # -Parameter beschrieben werden. Die Anzahl der für Parameter 1 reservierten Bits wird mit dem Schlüsselwort # $ P1B beschrieben. Es kann nur einen Satz von Listenmodusdaten pro Datensatz geben. Das Schlüsselwort $ DATATYPE # beschreibt das Datenformat. Dies ist der vielseitigste Modus für die Speicherung von Durchflusszytometriedaten, da Daten für Modus C und Modus U aus den Daten des Modus L erstellt werden können. Info info ["$ MODE"] == "L" -Parameter = [] # Indizes der parametersp_indices = Bereich (1, int (info ["$ PAR"]) + 1) für i in p_indices: p_name = info ["$ P% dN"% i] p_bits = info ["$ P% dB"% i] p_range = info ["$ P% dR"% i] p_ampl = info ["$ P% dE"% i] parameters.append (Parameter (p_name, p_bits, p_range, p_ampl)) sys.stdout.write ("Die Parameter sind: \ n% s \ n "%" \ t ".join ([par.p_name für par in Parametern])) # Wie werden 32-Bit-Wörter organisiert, wenn info [" $ BYTEORD "] ==" 4,3,2 , 1 ": endianness =" > "else: endianness =" < "assert info [" $ BYTEORD "] ==" 1,2,3,4 "# Ich habe einen langen Kommentar entfernt, der nur eine Kopie der Dokumentation ist # Datentyp: if info ["$ DATATYPE"] == "I": für Par in Parametern: nb_bits = int (par.p_bits) assert nb_bits% 8 == 0 nb_bytes = nb_bits / 8 # Bestimmen Sie die Formatzeichenfolge für das Entpacken ng (siehe https://docs.python.org/2/library/struct.html) if nb_bytes == 1: c_type = "B" # unsigned char elif nb_bytes == 2: c_type = "H" # unsigned short elif nb_bytes == 4: c_type = "L" # unsigned long elif nb_bytes == 8: c_type = "Q" # unsigned long long else:
erhöhen Sie ValueError, "Anzahl der Bytes (% d), die für eine Ganzzahl nicht gültig sind (siehe https://docs.python.org/2/library/struct.html#byte-order-size-and-alignment)." % nb_bytes fmt = "% s% s"% (Endianness, c_type) p_range = int (par.p_range) def Parser (Daten): value = struct.unpack (fmt, data.read (nb_bytes)) [0] try: assert value < p_range außer AssertionError: print "Wert% s höher als% d"% (str (Wert), p_range) Rückgabewert par.parser = parser passelse: erhöhe NotImplementedError, "Bisher wurde nur das Parsen des ganzzahligen Werts implementiert . "out_file = open (sys.argv [2]," w ") out_file.write (" # amplification_types \ t "+" \ t ".join ([par.p_ampl für par in parameter]) +" \ n " ) out_file.write ("parameters \ t" + "\ t" .join ([par.p_name für par in parameters]) + "\ n") i = 1 ############# #################################### Hier beginnt das Parsen der eigentlichen Daten ######## ####################################### f.seek (data_start) während f.tell ( ) < data_end: values = [] für par in parameters: values.append (par.parser (f)) out_f ile.write ("% d \ t"% i + "\ t" .join (map (str, values)) + "\ n") i + = 1out_file.close () f.close ()