$npx -y skills add K-Dense-AI/scientific-agent-skills --skill flowioParse FCS (Flow Cytometry Standard) files v2.0-3.1. Extract events as NumPy arrays, read metadata/channels, convert to CSV/DataFrame, for flow cytometry data preprocessing.
| 1 | # FlowIO: Flow Cytometry Standard File Handler |
| 2 | |
| 3 | ## Overview |
| 4 | |
| 5 | FlowIO is a lightweight Python library for reading and writing Flow Cytometry Standard (FCS) files. Parse FCS metadata, extract event data, and create new FCS files with minimal dependencies. The library supports FCS versions 2.0, 3.0, and 3.1, making it ideal for backend services, data pipelines, and basic cytometry file operations. |
| 6 | |
| 7 | ## When to Use This Skill |
| 8 | |
| 9 | This skill should be used when: |
| 10 | |
| 11 | - FCS files requiring parsing or metadata extraction |
| 12 | - Flow cytometry data needing conversion to NumPy arrays |
| 13 | - Event data requiring export to FCS format |
| 14 | - Multi-dataset FCS files needing separation |
| 15 | - Channel information extraction (scatter, fluorescence, time) |
| 16 | - Cytometry file validation or inspection |
| 17 | - Pre-processing workflows before advanced analysis |
| 18 | |
| 19 | **Related Tools:** For advanced flow cytometry analysis including compensation, gating, and FlowJo/GatingML support, recommend FlowKit library as a companion to FlowIO. |
| 20 | |
| 21 | ## Installation |
| 22 | |
| 23 | ```bash |
| 24 | uv pip install flowio |
| 25 | ``` |
| 26 | |
| 27 | Requires Python 3.9 or later. |
| 28 | |
| 29 | ## Quick Start |
| 30 | |
| 31 | ### Basic File Reading |
| 32 | |
| 33 | ```python |
| 34 | from flowio import FlowData |
| 35 | |
| 36 | # Read FCS file |
| 37 | flow_data = FlowData('experiment.fcs') |
| 38 | |
| 39 | # Access basic information |
| 40 | print(f"FCS Version: {flow_data.version}") |
| 41 | print(f"Events: {flow_data.event_count}") |
| 42 | print(f"Channels: {flow_data.pnn_labels}") |
| 43 | |
| 44 | # Get event data as NumPy array |
| 45 | events = flow_data.as_array() # Shape: (events, channels) |
| 46 | ``` |
| 47 | |
| 48 | ### Creating FCS Files |
| 49 | |
| 50 | ```python |
| 51 | import numpy as np |
| 52 | from flowio import create_fcs |
| 53 | |
| 54 | # Prepare data |
| 55 | data = np.array([[100, 200, 50], [150, 180, 60]]) # 2 events, 3 channels |
| 56 | channels = ['FSC-A', 'SSC-A', 'FL1-A'] |
| 57 | |
| 58 | # Create FCS file |
| 59 | create_fcs('output.fcs', data, channels) |
| 60 | ``` |
| 61 | |
| 62 | ## Core Workflows |
| 63 | |
| 64 | ### Reading and Parsing FCS Files |
| 65 | |
| 66 | The FlowData class provides the primary interface for reading FCS files. |
| 67 | |
| 68 | **Standard Reading:** |
| 69 | |
| 70 | ```python |
| 71 | from flowio import FlowData |
| 72 | |
| 73 | # Basic reading |
| 74 | flow = FlowData('sample.fcs') |
| 75 | |
| 76 | # Access attributes |
| 77 | version = flow.version # '3.0', '3.1', etc. |
| 78 | event_count = flow.event_count # Number of events |
| 79 | channel_count = flow.channel_count # Number of channels |
| 80 | pnn_labels = flow.pnn_labels # Short channel names |
| 81 | pns_labels = flow.pns_labels # Descriptive stain names |
| 82 | |
| 83 | # Get event data |
| 84 | events = flow.as_array() # Preprocessed (gain, log scaling applied) |
| 85 | raw_events = flow.as_array(preprocess=False) # Raw data |
| 86 | ``` |
| 87 | |
| 88 | **Memory-Efficient Metadata Reading:** |
| 89 | |
| 90 | When only metadata is needed (no event data): |
| 91 | |
| 92 | ```python |
| 93 | # Only parse TEXT segment, skip DATA and ANALYSIS |
| 94 | flow = FlowData('sample.fcs', only_text=True) |
| 95 | |
| 96 | # Access metadata |
| 97 | metadata = flow.text # Dictionary of TEXT segment keywords |
| 98 | print(metadata.get('$DATE')) # Acquisition date |
| 99 | print(metadata.get('$CYT')) # Instrument name |
| 100 | ``` |
| 101 | |
| 102 | **Handling Problematic Files:** |
| 103 | |
| 104 | Some FCS files have offset discrepancies or errors: |
| 105 | |
| 106 | ```python |
| 107 | # Ignore offset discrepancies between HEADER and TEXT sections |
| 108 | flow = FlowData('problematic.fcs', ignore_offset_discrepancy=True) |
| 109 | |
| 110 | # Use HEADER offsets instead of TEXT offsets |
| 111 | flow = FlowData('problematic.fcs', use_header_offsets=True) |
| 112 | |
| 113 | # Ignore offset errors entirely |
| 114 | flow = FlowData('problematic.fcs', ignore_offset_error=True) |
| 115 | ``` |
| 116 | |
| 117 | **Excluding Null Channels:** |
| 118 | |
| 119 | ```python |
| 120 | # Exclude specific channels during parsing |
| 121 | flow = FlowData('sample.fcs', null_channel_list=['Time', 'Null']) |
| 122 | ``` |
| 123 | |
| 124 | ### Extracting Metadata and Channel Information |
| 125 | |
| 126 | FCS files contain rich metadata in the TEXT segment. |
| 127 | |
| 128 | **Common Metadata Keywords:** |
| 129 | |
| 130 | ```python |
| 131 | flow = FlowData('sample.fcs') |
| 132 | |
| 133 | # File-level metadata |
| 134 | text_dict = flow.text |
| 135 | acquisition_date = text_dict.get('$DATE', 'Unknown') |
| 136 | instrument = text_dict.get('$CYT', 'Unknown') |
| 137 | data_type = flow.data_type # 'I', 'F', 'D', 'A' |
| 138 | |
| 139 | # Channel metadata |
| 140 | for i in range(flow.channel_count): |
| 141 | pnn = flow.pnn_labels[i] # Short name (e.g., 'FSC-A') |
| 142 | pns = flow.pns_labels[i] # Descriptive name (e.g., 'Forward Scatter') |
| 143 | pnr = flow.pnr_values[i] # Range/max value |
| 144 | print(f"Channel {i}: {pnn} ({pns}), Range: {pnr}") |
| 145 | ``` |
| 146 | |
| 147 | **Channel Type Identification:** |
| 148 | |
| 149 | FlowIO automatically categorizes channels: |
| 150 | |
| 151 | ```python |
| 152 | # Get indices by channel type |
| 153 | scatter_idx = flow.scatter_indices # [0, 1] for FSC, SSC |
| 154 | fluoro_idx = flow.fluoro_indices # [2, 3, 4] for FL channels |
| 155 | time_idx = flow.time_index # Index of time channel (or None) |
| 156 | |
| 157 | # Access specific channel types |
| 158 | events = flow.as_array() |
| 159 | scatter_data = events[:, scatter_idx] |
| 160 | fluorescence_data = events[:, fluoro_idx] |
| 161 | ``` |
| 162 | |
| 163 | **ANALYSIS Segment:** |
| 164 | |
| 165 | If present, access processed results: |
| 166 | |
| 167 | ```python |
| 168 | if flow.analysis: |
| 169 | analysis_keywords = flow.analysis # Dictionary of ANALYSIS keywords |
| 170 | pr |