Alcuni anni fa, ho scritto uno script Python per convertire i file FCS in un formato separato da tabulazioni. Era ben lungi dal gestire tutte le possibilità offerte dalla descrizione del formato, ma almeno ha funzionato per alcuni dei file prodotti su una delle nostre macchine: http://www.igh.cnrs.fr/equip/Seitz/ en_equipe-programmes.html
La documentazione del formato che ho trovato abilita la decodifica (vedere la sezione 3 del pdf di cui parli), ma richiede la lettura dei dati in modalità binaria.
L'idea generale di questo formato (e, immagino, molti altri formati binari) è che c'è una zona di intestazione all'inizio del file con un numero definito di campi che codificano numeri che indicano come è strutturato il resto del file. Quindi una prima fase è analizzare questa intestazione, seguendo la descrizione data nella documentazione del formato. Le informazioni estratte dall'intestazione indicano dove trovare i dati e come sono codificati, sempre secondo le regole descritte nella documentazione del formato.
Nel caso questo possa essere utile, e per la cronaca, ecco il codice dallo script sopra menzionato (dopo aver rimosso i commenti, alcuni dei quali sono semplicemente copiati dalla documentazione del formato, e aggiungendone alcuni):
#! / usr / bin / env python "" "Questo script cerca di leggere i dati della citometria a flusso FCS. Analisi del formato ispirata alle informazioni trovate qui: http: //isac-net.org/Resources-for-Cytometrists/Data-Standards/ Data-File-Standards / Flow-Cytometry-Data-File-Format-Standards.aspx "" "import re # Per decodificare dataimport con codifica binaria structimport sysclass Parametro (oggetto):" "" Questo oggetto rappresenta uno dei tipi di parametro che sono presenti in un segmento DATA di un file FCS. "" "__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
# Funzione per analizzare un valore del parametro nel segmento dati self.parser = Nessuno ############################### ############### Qui inizia l'analisi della parte dell'intestazione ## che indica dove sono le altre parti. ############################################## f = aperto (sys.argv [1], "rb") # Il nome del formato è codificato in 6 lettere # Una lettera ASCII è codificata con un octetfile_format = "" .join ([f.read (1) for __ nell'intervallo (6) ]) sys.stdout.write ("Formato:% s \ n"% file_format) # Le descrizioni del formato riservano 4 ottetti che saltiamo = f.read (4) # 8 blocchi di ottetti codificano le posizioni di inizio e fine # di parti diverse del 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 e analysis_end): sys.stderr.write ("Impossibile gestire il segmento ANALYSIS di un file FCS. \ n") ################## ################################## Qui inizia l'analisi della parte "TEXT" ## che descrive come i dati corretti sono organizzati ########################################### ######## f.seek (text_start) # Il primo carattere nel segmento TEXT principale è il carattere delimitatore ASCII.sep = f.read (1) se sep non è in ["_", "@"]: alt_sep = "_ @ _" elif sep non in ["_", " | "]: alt_sep =" _ | _ "else: assert sep non in [" + "," | "] alt_sep =" + | + "text_segment = f.read (text_end - text_start) fields = text_segment.split (sep ) info = {} i = 0 while i < len (fields) - 1: key = fields [i] i + = 1 val = fields [i] i + = 1 # Le parole chiave non fanno distinzione tra maiuscole e minuscole, possono essere scritte in un file in lettere minuscole, maiuscole o un # misto dei due. Tuttavia, un lettore di file FCS deve ignorare le maiuscole / minuscole. Il valore di una parola chiave può # essere in lettere minuscole, maiuscole o una combinazione delle due. I valori delle parole chiave fanno distinzione tra maiuscole e minuscole. info [key.upper ()] = val
print "Sono stati rilevati% s eventi." % info ["$ TOT"] print "Ogni evento è caratterizzato da% s parametri"% info ["$ PAR"] if info ["$ NEXTDATA"]! = "0": sys.stderr.write ("Some other i dati esistono nel file ma non sono stati analizzati. \ n ") # L - Modalità elenco. Per ogni evento, il valore di ogni parametro viene memorizzato nell'ordine in cui sono descritti i parametri #. Il numero di bit riservati per il parametro 1 è descritto utilizzando la parola chiave # $ P1B. Può esserci un solo set di dati in modalità elenco per set di dati. La parola chiave $ DATATYPE # descrive il formato dei dati. Questa è la modalità più versatile per la memorizzazione dei dati di citometria a flusso # perché i dati della modalità C e della modalità U possono essere creati dalla modalità L data.assert info ["$ MODE"] == "L" parameters = [] # indici del parametersp_indices = range (1, int (info ["$ PAR"]) + 1) for 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 ("I parametri sono: \ n% s \ n "%" \ t ".join ([par.p_name for par in parameters])) # Come sono organizzate le parole a 32 bit se info [" $ BYTEORD "] ==" 4,3,2 , 1 ": endianness =" > "else: endianness =" < "assert info [" $ BYTEORD "] ==" 1,2,3,4 "# Ho rimosso un lungo commento che è solo una copia della documentazione # Tipo di dati: if info ["$ DATATYPE"] == "I": per par nei parametri: nb_bits = int (par.p_bits) asserire nb_bits% 8 == 0 nb_bytes = nb_bits / 8 # Determina la stringa di formato per unpacki ng (vedi https://docs.python.org/2/library/struct.html) if nb_bytes == 1: c_type = "B" # char elif unsigned 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:
raise ValueError, "Numero di byte (% d) non valido per un intero (vedi 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 (data): value = struct.unpack (fmt, data.read (nb_bytes)) [0] prova: asserisci valore < p_range eccetto AssertionError: print "Valore% s maggiore di% d"% (str (value), p_range) valore restituito par.parser = parser passelse: raise NotImplementedError, "Finora è stata implementata solo l'analisi del valore intero . "out_file = open (sys.argv [2]," w ") out_file.write (" # amplification_types \ t "+" \ t ".join ([par.p_ampl per par in parametri]) +" \ n " ) out_file.write ("parameters \ t" + "\ t" .join ([par.p_name for par in parameters]) + "\ n") i = 1 ############# ################################# Qui inizia l'analisi dei dati propri ######## ###################################### f.seek (data_start) mentre f.tell ( ) < data_end: values = [] for 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 ()