class FCSParse::FCSFile

A class representing an FCS-encoded file. Has methods to parse the data, manage it, and convert it to human-readable format.

Currently reads FCS v3.x files. Files must have data encoded in list mode, and data points must be in float or double format (this will change eventually to support all formats in the specification).

Analysis sections of the file are ignored.

Data can be written to delimited text files, and metadata to plain text files, or the objects containing the data can be used by other code to process the data further.

@author Colin J. Fuller

Constants

DataDelimiter
DataExtension
MetadataExtension
SupportedVersions

Attributes

events[RW]
filename[RW]

Public Class Methods

new(file_string) click to toggle source

Generates a new FCSFile from the specified FCS-encoded data string.

@param [String] file_string a String containing an FCS-encoded dataset.

# File lib/fcsparse.rb, line 87
def initialize(file_string)
  @data = file_string
  @keywords = Hash.new
end
new_from_file(filename) click to toggle source

Generates a new FCSFile object from the specified file.

Calling this will read the entire file into memory but will not parse it.

@param [String] filename the filename of the FCS-encoded file (with path as required to locate it) @return [FCSFile] a new FCSFile object initialized with the contents of the file.

# File lib/fcsparse.rb, line 69
def self.new_from_file(filename)
  
  fcsfile = nil
  
  File.open(filename) do |f|
    fcsfile = self.new(f.read)
  end
  
  fcsfile.filename = filename
  
  fcsfile
  
end

Public Instance Methods

get_metadata_string() click to toggle source

Gets the metadata (that is all the information in the text block of the fcs file) as a string, where there is one key, value pair per line, separated by the characters “ => ”

@return [String] a String containing all the metadata

# File lib/fcsparse.rb, line 252
def get_metadata_string
  str = ""
  @keywords.keys.map{|e| e.to_s}.sort.each do |k|
    str << k.to_s + " => " + @keywords[k.to_sym].to_s + "\n"
  end
  str
end
parse() click to toggle source

Parses the raw data loaded in the FCSFile to metadata, events, and parameters.

Erases the raw data from the FCSFile object after parsing is complete.

# File lib/fcsparse.rb, line 324
def parse
  read_header
  parse_text_segment
  parse_data_segment
  @data = nil
end
print_metadata() click to toggle source

Prints the metadata string returned by {#get_metadata_string}.

write_metadata_and_data(write_header_row = true) click to toggle source

Writes the metadata and data from the FCS-formatted file to disk in human readable format.

The metadata is written as a text file (formatted by {#get_metadata_string}), in the same location as the input FCS file and with the same name except for an extra extension specified in {FCSFile::MetadataExtension}.

The data, delimited by the data delimiter specified as {FCSFile::DataDelimiter}, one row per event, is written to a text file in the same location as the input FCS file and with the same name except for an extra extension specified in {FCSFile::DataExtension}.

@param [Boolean] write_header_row an optional parameter specifying whether the data file should have a header row with the name of each column’s parameter. Defaults to true.

# File lib/fcsparse.rb, line 289
def write_metadata_and_data(write_header_row = true)
  
  meta_filename = @filename + MetadataExtension
  
  data_filename = @filename + DataExtension
  
  File.open(meta_filename, "w") do |f|
    
    f.write(get_metadata_string)
    
  end
  
  File.open(data_filename, "w") do |f|
    
    if write_header_row then
      f.puts(@events[0].names_to_s_delim(DataDelimiter))
    end
    
    @events.each do |e|
      
      f.puts(e.to_s_delim(DataDelimiter))
    
    end
    
  end
  
  nil
  
end

Private Instance Methods

construct_event_format_string() click to toggle source
# File lib/fcsparse.rb, line 194
def construct_event_format_string
  
  datatype = @keywords[T_DatatypeKeyword]
  
  is_little_endian = (@keywords[T_ByteorderKeyword] == T_LittleEndianByteorder)
        
  formatchar = case datatype
  
  when 'I'
    
    nil
     
  when 'F'
    
    if is_little_endian then
      
      'e'
      
    else
      
      'g'
      
    end
    
  when 'D'
    
    if is_little_endian then
      
      'E'
      
    else
      
      'G'
      
    end
    
  when 'A'
    
    nil
    
  end
    
  unless formatchar then
    raise "Integer and string data not yet supported."
  end
        
  parameter_count = @keywords[T_ParameterCountKeyword].to_i
        
  formatchar*parameter_count
  
end
parse_data_segment() click to toggle source
# File lib/fcsparse.rb, line 170
def parse_data_segment
  
  event_format = construct_event_format_string
  
  event_count = @keywords[T_EventCountKeyword].to_i
  
  bytes_per_event = ((@data_offsets[1] - @data_offsets[0] + 1)*1.0/event_count).to_i
        
  @events = Array.new
        
  0.upto(event_count -1) do |e|
    
    offset = @data_offsets[0] + bytes_per_event*e
    
    event_string = @data[offset, bytes_per_event]
    
    event = FCSEvent.new_with_data_and_format(event_string, event_format, @keywords)
    
    @events << event
            
  end
  
end
parse_text_segment() click to toggle source
# File lib/fcsparse.rb, line 147
def parse_text_segment
  
  parse_text_segment_with_bounds(@text_offsets[0].to_i, @text_offsets[1].to_i)
  
  suppl_text_start = @keywords[T_SupplTextStartKeyword].to_i
  suppl_text_end = @keywords[T_SupplTextEndKeyword].to_i
  
  unless suppl_text_start == 0 and suppl_text_end == 0 then
    parse_text_segment_with_bounds(suppl_text_start, suppl_text_end)
  end
  
  @data_offsets[0] = @keywords[T_DataStartKeyword].to_i
  @data_offsets[1] = @keywords[T_DataEndKeyword].to_i
  
  @analysis_offsets[0] = @keywords[T_AnalysisStartKeyword].to_i
  @analysis_offsets[1] = @keywords[T_AnalysisEndKeyword].to_i
  
  unless @keywords[T_ModeKeyword] == "L" then
    raise "Only list mode is supported for the data block."
  end
  
end
parse_text_segment_with_bounds(lower_bound, upper_bound) click to toggle source
# File lib/fcsparse.rb, line 118
def parse_text_segment_with_bounds(lower_bound, upper_bound)
  
  token_queue = Array.new
  
  delimiter = @data[lower_bound]
  
  token_start = lower_bound + 1
  
  (lower_bound+1).upto(upper_bound) do |i|
    
    if @data[i] == delimiter and not @data[i-1] == delimiter and not @data[i+1] == delimiter then
      
      token_queue << @data[token_start...i]
      token_start = i+1
      
    end
    
  end
  
  token_queue.each_slice(2) do |kv|
    
    @keywords[kv[0].upcase.to_sym]= kv[1]
    
  end
  
  

end
read_header() click to toggle source
# File lib/fcsparse.rb, line 92
def read_header

  version_string= @data[H_VersionStart..H_VersionEnd]

  unless SupportedVersions.include?(version_string) then
    raise "Unable to read this FCS format: " + version_string
  end
  

  @version = version_string[3..5].to_f
  
  @text_offsets = Array.new
  @data_offsets = Array.new
  @analysis_offsets = Array.new
  
  @text_offsets << @data[H_TextBlockOffsetStart, H_OffsetBlockLength].to_i
  @text_offsets << @data[H_TextBlockOffsetEnd, H_OffsetBlockLength].to_i
  
  @data_offsets << @data[H_DataBlockOffsetStart, H_OffsetBlockLength].to_i
  @data_offsets << @data[H_DataBlockOffsetEnd, H_OffsetBlockLength].to_i
  
  @analysis_offsets << @data[H_AnalysisBlockOffsetStart, H_OffsetBlockLength].to_i
  @analysis_offsets << @data[H_AnalysisBlockOffsetEnd, H_OffsetBlockLength].to_i
        
end