pub struct Fcs {
pub header: Header,
pub metadata: Metadata,
pub parameters: FxHashMap<ChannelName, Parameter>,
pub data_frame: Arc<DataFrame>,
pub file_access: AccessWrapper,
}Expand description
A struct representing an FCS file
Fields§
§header: HeaderThe header segment of the fcs file, including the version, and byte offsets to the text, data, and analysis segments
metadata: MetadataThe metadata segment of the fcs file, including the delimiter, and a hashmap of keyword/value pairs
parameters: FxHashMap<ChannelName, Parameter>A hashmap of the parameter names and their associated metadata
data_frame: Arc<DataFrame>Event data stored in columnar format via Polars DataFrame (NEW) Each column represents one parameter (e.g., FSC-A, SSC-A, FL1-A) Polars provides:
- Zero-copy column access
- Built-in SIMD operations
- Lazy evaluation for complex queries
- Apache Arrow interop This is the primary data format going forward
file_access: AccessWrapperA wrapper around the file, path, and memory-map
Implementations§
Source§impl Fcs
impl Fcs
Sourcepub fn new() -> Result<Self>
pub fn new() -> Result<Self>
Creates a new Fcs file struct
§Errors
Will return Err if:
- the file cannot be opened,
- the file extension is not
fcs, - the TEXT segment cannot be validated,
- the raw data cannot be read,
- the parameter names and labels cannot be generated
Sourcepub fn open(path: &str) -> Result<Self>
pub fn open(path: &str) -> Result<Self>
Opens and parses an FCS file from the given path
This is the primary entry point for reading FCS files. It:
- Validates the file extension (must be
.fcs) - Memory-maps the file for efficient access
- Parses the header segment to determine FCS version and segment offsets
- Parses the text segment to extract metadata and keywords
- Validates required keywords for the FCS version
- Generates a GUID if one is not present
- Loads event data into a Polars DataFrame for efficient columnar access
§Arguments
path- Path to the FCS file (must have.fcsextension)
§Errors
Will return Err if:
- the file cannot be opened or memory-mapped
- the file extension is not
.fcs - the FCS version is invalid or unsupported
- required keywords are missing for the FCS version
- the data segment cannot be read or parsed
- parameter metadata cannot be generated
§Example
use flow_fcs::Fcs;
let fcs = Fcs::open("data/sample.fcs")?;
println!("File has {} events", fcs.get_number_of_events()?);Sourcepub fn find_parameter(&self, parameter_name: &str) -> Result<&Parameter>
pub fn find_parameter(&self, parameter_name: &str) -> Result<&Parameter>
Looks for the parameter name as a key in the parameters hashmap and returns a reference to it
Performs case-insensitive lookup for parameter names
§Errors
Will return Err if the parameter name is not found in the parameters hashmap
Sourcepub fn find_mutable_parameter(
&mut self,
parameter_name: &str,
) -> Result<&mut Parameter>
pub fn find_mutable_parameter( &mut self, parameter_name: &str, ) -> Result<&mut Parameter>
Looks for the parameter name as a key in the parameters hashmap and returns a mutable reference to it
Performs case-insensitive lookup for parameter names
§Errors
Will return Err if the parameter name is not found in the parameters hashmap
Sourcepub fn get_parameter_events(
&self,
channel_name: &str,
) -> Result<&Float32Chunked>
pub fn get_parameter_events( &self, channel_name: &str, ) -> Result<&Float32Chunked>
Returns a zero-copy reference to a Polars Float32Chunked view of a column for the parameter
This provides access to the underlying Polars chunked array, which is useful
for operations that work directly with Polars types. For most use cases,
get_parameter_events_slice() is preferred as it provides a simple &[f32] slice.
§Arguments
channel_name- The channel name (e.g., “FSC-A”, “FL1-A”)
§Errors
Will return Err if:
- the parameter name is not found in the parameters map
- the column data type is not Float32
Sourcepub fn get_parameter_column(&self, channel_name: &str) -> Result<&Column>
pub fn get_parameter_column(&self, channel_name: &str) -> Result<&Column>
Get a reference to the Polars Column for a parameter by channel name
This provides direct access to the underlying Polars column, which can be useful for advanced operations that require the full Polars API.
§Arguments
channel_name- The channel name (e.g., “FSC-A”, “FL1-A”)
§Errors
Will return Err if the parameter name is not found in the DataFrame
Sourcepub fn get_parameter_events_as_owned_vec(
&self,
channel_name: &str,
) -> Result<Vec<f32>>
pub fn get_parameter_events_as_owned_vec( &self, channel_name: &str, ) -> Result<Vec<f32>>
Looks for the parameter name as a key in the ‘parameters’ hashmap and returns a new Vecget_parameter_events_slice when possible
§Errors
Will return ‘Err’ if the parameter name is not found in the ’parameters hashmap or if the events are not found
Sourcepub fn get_minmax_of_parameter(&self, channel_name: &str) -> Result<(f32, f32)>
pub fn get_minmax_of_parameter(&self, channel_name: &str) -> Result<(f32, f32)>
Returns the minimum and maximum values of the parameter
§Errors
Will return Err if the parameter name is not found in the ‘parameters’ hashmap or if the events are not found
Sourcepub fn generate_parameter_map(
metadata: &Metadata,
) -> Result<FxHashMap<ChannelName, Parameter>>
pub fn generate_parameter_map( metadata: &Metadata, ) -> Result<FxHashMap<ChannelName, Parameter>>
Creates a new HashMap of Parameters
using the Fcs file’s metadata to find the channel and label names from the PnN and PnS keywords.
Does NOT store events on the parameter.
§Errors
Will return Err if:
- the number of parameters cannot be found in the metadata,
- the parameter name cannot be found in the metadata,
- the parameter cannot be built (using the Builder pattern)
Sourcepub fn get_keyword_string_value(&self, keyword: &str) -> Result<Cow<'_, str>>
pub fn get_keyword_string_value(&self, keyword: &str) -> Result<Cow<'_, str>>
Looks for a keyword among the metadata and returns its value as a &str
§Errors
Will return Err if the Keyword is not found in the metadata or if the Keyword cannot be converted to a &str
Sourcepub fn get_guid(&self) -> Result<Cow<'_, str>>
pub fn get_guid(&self) -> Result<Cow<'_, str>>
A convenience function to return the GUID keyword from the metadata as a &str
§Errors
Will return Err if the GUID keyword is not found in the metadata or if the GUID keyword cannot be converted to a &str
Sourcepub fn get_fil_keyword(&self) -> Result<Cow<'_, str>>
pub fn get_fil_keyword(&self) -> Result<Cow<'_, str>>
A convenience function to return the $FIL keyword from the metadata as a &str
§Errors
Will return Err if the $FIL keyword is not found in the metadata or if the $FIL keyword cannot be converted to a &str
Sourcepub fn get_number_of_events(&self) -> Result<&usize>
pub fn get_number_of_events(&self) -> Result<&usize>
A convenience function to return the $TOT keyword from the metadata as a usize
§Errors
Will return Err if the $TOT keyword is not found in the metadata or if the $TOT keyword cannot be converted to a usize
Sourcepub fn get_number_of_parameters(&self) -> Result<&usize>
pub fn get_number_of_parameters(&self) -> Result<&usize>
A convenience function to return the $PAR keyword from the metadata as a usize
§Errors
Will return Err if the $PAR keyword is not found in the metadata or if the $PAR keyword cannot be converted to a usize
Sourcepub fn get_parameter_events_slice(&self, channel_name: &str) -> Result<&[f32]>
pub fn get_parameter_events_slice(&self, channel_name: &str) -> Result<&[f32]>
Get events for a parameter as a slice of f32 values Polars gives us direct access to the underlying buffer (zero-copy)
§Errors
Will return Err if:
- the parameter name is not found
- the Series data type is not Float32
- the data is chunked (rare for FCS files)
Sourcepub fn get_xy_pairs(
&self,
x_param: &str,
y_param: &str,
) -> Result<Vec<(f32, f32)>>
pub fn get_xy_pairs( &self, x_param: &str, y_param: &str, ) -> Result<Vec<(f32, f32)>>
Get two parameters as (x, y) pairs for plotting Optimized for scatter plot use case with zero allocations until the collect
§Errors
Will return Err if either parameter name is not found
Sourcepub fn get_event_count_from_dataframe(&self) -> usize
pub fn get_event_count_from_dataframe(&self) -> usize
Get DataFrame height (number of events)
Sourcepub fn get_parameter_count_from_dataframe(&self) -> usize
pub fn get_parameter_count_from_dataframe(&self) -> usize
Get DataFrame width (number of parameters)
Sourcepub fn get_parameter_names_from_dataframe(&self) -> Vec<String>
pub fn get_parameter_names_from_dataframe(&self) -> Vec<String>
Get DataFrame column names (parameter names)
Sourcepub fn get_parameter_statistics(
&self,
channel_name: &str,
) -> Result<(f32, f32, f32, f32)>
pub fn get_parameter_statistics( &self, channel_name: &str, ) -> Result<(f32, f32, f32, f32)>
Aggregate statistics for a parameter using Polars’ streaming API for low memory usage and minimal, chunked passes.
When streaming is enabled, Polars creates a Pipeline:
Source: It pulls a chunk of data from the disk (e.g., 50,000 rows).
Operators: It passes that chunk through your expressions (calculating the running sum, count, min, and max for that specific chunk).
Sink: It aggregates the results from all chunks into a final result.
Because the statistics we are calculating (min, max, mean) are associative and commutative, Polars can calculate them partially on each chunk and then combine them at the very end.
Returns (min, max, mean, std_dev)
§Errors
Will return Err if the parameter is not found or stats calculation fails
Sourcepub fn apply_arcsinh_transform(
&self,
parameter_name: &str,
cofactor: f32,
) -> Result<Arc<DataFrame>>
pub fn apply_arcsinh_transform( &self, parameter_name: &str, cofactor: f32, ) -> Result<Arc<DataFrame>>
Apply arcsinh transformation to a parameter using Polars This is the most common transformation for flow cytometry data Formula: arcsinh(x / cofactor) / ln(10)
§Arguments
parameter_name- Name of the parameter to transformcofactor- Scaling factor (typical: 150-200 for modern instruments)
§Returns
New DataFrame with the transformed parameter
Sourcepub fn apply_arcsinh_transforms(
&self,
parameters: &[(&str, f32)],
) -> Result<Arc<DataFrame>>
pub fn apply_arcsinh_transforms( &self, parameters: &[(&str, f32)], ) -> Result<Arc<DataFrame>>
Sourcepub fn apply_default_arcsinh_transform(&self) -> Result<Arc<DataFrame>>
pub fn apply_default_arcsinh_transform(&self) -> Result<Arc<DataFrame>>
Apply default arcsinh transformation to all fluorescence parameters Automatically detects fluorescence parameters (excludes FSC, SSC, Time) Uses cofactor = 200 (good default for modern instruments)
Sourcepub fn get_spillover_matrix(&self) -> Result<Option<(Array2<f32>, Vec<String>)>>
pub fn get_spillover_matrix(&self) -> Result<Option<(Array2<f32>, Vec<String>)>>
Extract compensation matrix from $SPILLOVER keyword Returns (matrix, channel_names) if spillover keyword exists Returns None if no spillover keyword is present in the file
§Returns
Some((compensation_matrix, channel_names)) if spillover exists, None otherwise
§Errors
Will return Err if spillover keyword is malformed
Sourcepub fn has_compensation(&self) -> bool
pub fn has_compensation(&self) -> bool
Check if this file has compensation information
Sourcepub fn apply_file_compensation(&self) -> Result<Arc<DataFrame>>
pub fn apply_file_compensation(&self) -> Result<Arc<DataFrame>>
Apply compensation from the file’s $SPILLOVER keyword Convenience method that extracts spillover and applies it automatically
§Returns
New DataFrame with compensated data, or error if no spillover keyword exists
Sourcepub fn get_compensated_parameters(
&self,
channels_needed: &[&str],
) -> Result<HashMap<String, Vec<f32>>>
pub fn get_compensated_parameters( &self, channels_needed: &[&str], ) -> Result<HashMap<String, Vec<f32>>>
OPTIMIZED: Get compensated data for specific parameters only (lazy/partial compensation)
This is 15-30x faster than apply_file_compensation when you only need a few parameters because it:
- Only compensates the requested channels (e.g., 2 vs 30)
- Uses sparse matrix optimization for matrices with >80% zeros
- Bypasses compensation entirely for identity matrices
§Arguments
channels_needed- Only the channel names you need compensated (typically 2 for a plot)
§Returns
HashMap of channel_name -> compensated data (as Vec
§Performance
- Dense matrix (2/30 channels): 15x faster (150ms → 10ms)
- Sparse matrix (90% sparse): 50x faster (150ms → 3ms)
- Identity matrix: 300x faster (150ms → 0.5ms)
Sourcepub fn apply_compensation(
&self,
compensation_matrix: &Array2<f32>,
channel_names: &[&str],
) -> Result<Arc<DataFrame>>
pub fn apply_compensation( &self, compensation_matrix: &Array2<f32>, channel_names: &[&str], ) -> Result<Arc<DataFrame>>
Apply compensation matrix to the data using Polars Compensation corrects for spectral overlap between fluorescence channels
§Arguments
compensation_matrix- 2D matrix where element [i,j] represents spillover from channel j into channel ichannel_names- Names of channels in the order they appear in the matrix
§Returns
New DataFrame with compensated fluorescence values
§Example
// Create a 3x3 compensation matrix
let comp_matrix = Array2::from_shape_vec((3, 3), vec![
1.0, 0.1, 0.05, // FL1-A compensation
0.2, 1.0, 0.1, // FL2-A compensation
0.1, 0.15, 1.0, // FL3-A compensation
]).unwrap();
let channels = vec!["FL1-A", "FL2-A", "FL3-A"];
let compensated = fcs.apply_compensation(&comp_matrix, &channels)?;Sourcepub fn apply_spectral_unmixing(
&self,
unmixing_matrix: &Array2<f32>,
channel_names: &[&str],
cofactor: Option<f32>,
) -> Result<Arc<DataFrame>>
pub fn apply_spectral_unmixing( &self, unmixing_matrix: &Array2<f32>, channel_names: &[&str], cofactor: Option<f32>, ) -> Result<Arc<DataFrame>>
Apply spectral unmixing (similar to compensation but for spectral flow cytometry) Uses a good default cofactor of 200 for transformation before/after unmixing
§Arguments
unmixing_matrix- Matrix describing spectral signatures of fluorophoreschannel_names- Names of spectral channelscofactor- Cofactor for arcsinh transformation (default: 200)
§Returns
New DataFrame with unmixed and transformed fluorescence values
Trait Implementations§
Auto Trait Implementations§
impl Freeze for Fcs
impl !RefUnwindSafe for Fcs
impl Send for Fcs
impl Sync for Fcs
impl Unpin for Fcs
impl !UnwindSafe for Fcs
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more