[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6XF7E76RTMCNTF1JQ56B69Z":3,"skill-same:01M6XF7E76RTMCNTF1JQ56B69Z":85},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":39,"linkOnly":40,"markdownContent":41,"markdownBody":42,"frontMatter":43,"refName":47,"commitSha":48,"skillDirectory":49,"forksCount":14,"repositoryHtmlUrl":50,"repositoryHomepageUrl":6,"ownerHtmlUrl":51,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":52,"install":53,"resources":66},"01M6XF7E76RTMCNTF1JQ56B69Z","flow-cytometry-analysis",null,"Complete flow cytometry analysis pipeline. FCS file handling, compensation, manual\u002Fautomated gating, immunophenotyping, CFSE proliferation analysis, cell cycle analysis (Dean-Jett-Fox), and apoptosis assays. Extends flowio with analytical workflows. For raw FCS parsing only use flowio.","fadrienne\u002FMyGitHub","fadrienne","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F197874571?v=4","01M6X64V8VWCRSMFZ4EPS2BWXG","Francesca Adrienne","01M6X64VD5MW4F9PHDWQKS7ZAF",0,"MIT","https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002Fopenscience\u002Fbackend\u002Fcli\u002Fskills\u002Fbiology\u002Fflow-cytometry-analysis\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"flow cytometry analysis","flow","analysis","cell","compensation","gating","for","manual","with","biology","complete","file handling","workflows","cycle","only","use","parsing","flowio","pipeline","file","2026-11-10 10:12:30",false,"---\nname: flow-cytometry-analysis\ndescription: Complete flow cytometry analysis pipeline. FCS file handling, compensation, manual\u002Fautomated gating, immunophenotyping, CFSE proliferation analysis, cell cycle analysis (Dean-Jett-Fox), and apoptosis assays. Extends flowio with analytical workflows. For raw FCS parsing only use flowio.\ncategory: biology\nlicense: MIT license\nmetadata:\n    skill-author: InkVell Inc.\n---\n\n# Flow Cytometry Analysis: Complete Analysis Pipeline\n\n## Overview\n\nFlow Cytometry Analysis provides end-to-end computational workflows for analyzing flow cytometry data. Starting from FCS file parsing, through compensation matrix application, gating strategies (manual rectangular\u002Fpolygon gates and automated Gaussian mixture model gating), to downstream analyses including immunophenotyping, CFSE proliferation tracking, cell cycle phase quantification (Dean-Jett-Fox model), and apoptosis assays (Annexin V\u002FPI). This skill extends basic FCS file handling (flowio) with full analytical pipelines.\n\n## When to Use This Skill\n\n- Analyzing multi-color flow cytometry experiments\n- Applying compensation matrices to correct spectral overlap\n- Building sequential gating hierarchies (debris exclusion, singlet gating, live\u002Fdead, marker gating)\n- Immunophenotyping with multi-marker panels (CD3, CD4, CD8, etc.)\n- Quantifying cell proliferation from CFSE\u002FCellTrace dilution\n- Determining cell cycle phase distribution from DNA content histograms\n- Analyzing apoptosis from Annexin V \u002F propidium iodide staining\n- Creating density plots, histograms, and overlay visualizations\n- Automated gating for high-throughput cytometry experiments\n\n**Related Skills:** For raw FCS file parsing and creation use `flowio`. For single-cell RNA-seq analysis use `scanpy`.\n\n## Installation\n\n```bash\nuv pip install flowio fcsparser scipy numpy pandas matplotlib scikit-learn\n```\n\n## Quick Start\n\n```python\nfrom flowio import FlowData\nimport numpy as np\n\n# Read FCS file\nfcs = FlowData('sample.fcs')\nevents = fcs.as_array()\nchannels = fcs.pnn_labels\n\nprint(f\"Events: {events.shape[0]}, Channels: {len(channels)}\")\nprint(f\"Channels: {channels}\")\n\n# Simple FSC\u002FSSC gate (remove debris)\nfsc_idx = channels.index('FSC-A')\nssc_idx = channels.index('SSC-A')\nmask = (events[:, fsc_idx] > 50000) & (events[:, ssc_idx] > 10000)\ngated = events[mask]\nprint(f\"After gating: {gated.shape[0]} events ({100*mask.sum()\u002Flen(mask):.1f}%)\")\n```\n\n## Core Capabilities\n\n### 1. FCS File Handling\n\nRead FCS files and extract channel information.\n\n```python\nfrom flowio import FlowData\nimport fcsparser\nimport numpy as np\n\n# Method 1: flowio\nfcs = FlowData('sample.fcs')\nevents = fcs.as_array()\nchannel_names = fcs.pnn_labels\nstain_names = fcs.pns_labels\n\n# Method 2: fcsparser (returns metadata + DataFrame)\nmeta, data = fcsparser.parse('sample.fcs', reformat_meta=True)\nprint(f\"Channels: {list(data.columns)}\")\n\n# Extract compensation matrix from metadata\nif '$SPILLOVER' in fcs.text or 'SPILL' in fcs.text:\n    spill_str = fcs.text.get('$SPILLOVER', fcs.text.get('SPILL', ''))\n    print(\"Compensation matrix found in metadata\")\n```\n\n### 2. Compensation\n\nApply spectral overlap correction.\n\n```python\nimport numpy as np\n\ndef parse_spillover_matrix(spill_string):\n    \"\"\"Parse $SPILLOVER or SPILL keyword from FCS metadata.\"\"\"\n    parts = spill_string.split(',')\n    n = int(parts[0])\n    channel_names = parts[1:n+1]\n    values = [float(x) for x in parts[n+1:]]\n    matrix = np.array(values).reshape(n, n)\n    return channel_names, matrix\n\ndef compensate(events, fluoro_indices, spillover_matrix):\n    \"\"\"Apply compensation to fluorescence channels.\"\"\"\n    inv_spill = np.linalg.inv(spillover_matrix)\n    compensated = events.copy()\n    fluoro_data = events[:, fluoro_indices]\n    compensated[:, fluoro_indices] = fluoro_data @ inv_spill.T\n    return compensated\n\n# Apply compensation\nspill_str = fcs.text.get('$SPILLOVER', fcs.text.get('SPILL', ''))\nif spill_str:\n    comp_channels, spill_matrix = parse_spillover_matrix(spill_str)\n    fluoro_idx = [channel_names.index(ch) for ch in comp_channels]\n    events_comp = compensate(events, fluoro_idx, spill_matrix)\n    print(f\"Compensated {len(fluoro_idx)} fluorescence channels\")\n```\n\n### 3. Gating Strategies\n\nApply sequential gates to identify cell populations.\n\n**Manual rectangular gates:**\n\n```python\nimport numpy as np\n\ndef rect_gate(events, ch_idx, lo, hi):\n    \"\"\"Apply rectangular gate on one channel.\"\"\"\n    return (events[:, ch_idx] >= lo) & (events[:, ch_idx] \u003C= hi)\n\ndef polygon_gate(events, x_idx, y_idx, vertices):\n    \"\"\"Apply polygon gate using ray casting.\"\"\"\n    from matplotlib.path import Path\n    points = np.column_stack([events[:, x_idx], events[:, y_idx]])\n    poly = Path(vertices)\n    return poly.contains_points(points)\n\n# Sequential gating tree\n# Step 1: FSC\u002FSSC - remove debris\nscatter_gate = rect_gate(events, fsc_idx, 30000, 250000) & \\\n               rect_gate(events, ssc_idx, 5000, 200000)\n\n# Step 2: Singlet gate (FSC-A vs FSC-H)\nfsch_idx = channel_names.index('FSC-H')\nsinglet_gate = scatter_gate.copy()\nratio = events[:, fsc_idx] \u002F (events[:, fsch_idx] + 1)\nsinglet_gate &= (ratio > 0.8) & (ratio \u003C 1.2)\n\n# Step 3: Live gate (exclude viability dye positive)\n# live_gate = singlet_gate & rect_gate(events, viability_idx, 0, threshold)\n\nprint(f\"Debris exclusion: {scatter_gate.sum()} events\")\nprint(f\"Singlets: {singlet_gate.sum()} events\")\n```\n\n**Automated gating with Gaussian Mixture Models:**\n\n```python\nfrom sklearn.mixture import GaussianMixture\nimport numpy as np\n\ndef auto_gate_gmm(events, channels_idx, n_components=2):\n    \"\"\"Automated gating using Gaussian Mixture Model.\"\"\"\n    data = events[:, channels_idx]\n    # Log transform for better separation\n    data_log = np.log1p(np.clip(data, 0, None))\n\n    gmm = GaussianMixture(n_components=n_components, random_state=42)\n    labels = gmm.fit_predict(data_log)\n\n    # Identify populations by mean intensity\n    means = gmm.means_\n    pop_order = np.argsort(means.sum(axis=1))\n\n    return labels, gmm, pop_order\n\n# Auto-gate lymphocytes vs debris\nlabels, gmm, order = auto_gate_gmm(events, [fsc_idx, ssc_idx], n_components=3)\nlymphocyte_mask = labels == order[1]  # Middle population is typically lymphocytes\nprint(f\"Lymphocyte gate: {lymphocyte_mask.sum()} events\")\n```\n\n### 4. Immunophenotyping\n\nMulti-marker panel analysis for population identification.\n\n```python\nimport numpy as np\nimport pandas as pd\n\ndef immunophenotype(events, channel_names, gates, parent_mask=None):\n    \"\"\"Calculate population frequencies from marker gates.\"\"\"\n    if parent_mask is None:\n        parent_mask = np.ones(len(events), dtype=bool)\n\n    results = {}\n    parent_count = parent_mask.sum()\n\n    for pop_name, marker_gates in gates.items():\n        pop_mask = parent_mask.copy()\n        for ch_name, threshold, direction in marker_gates:\n            ch_idx = channel_names.index(ch_name)\n            if direction == '+':\n                pop_mask &= events[:, ch_idx] > threshold\n            else:\n                pop_mask &= events[:, ch_idx] \u003C= threshold\n\n        count = pop_mask.sum()\n        freq = 100 * count \u002F parent_count if parent_count > 0 else 0\n        results[pop_name] = {'count': count, 'frequency': freq, 'mask': pop_mask}\n\n    return results\n\n# Define immunophenotyping panel\nphenotype_gates = {\n    'CD3+ T cells': [('CD3', 500, '+')],\n    'CD4+ T helper': [('CD3', 500, '+'), ('CD4', 300, '+'), ('CD8', 300, '-')],\n    'CD8+ T cytotoxic': [('CD3', 500, '+'), ('CD4', 300, '-'), ('CD8', 300, '+')],\n    'B cells (CD19+)': [('CD3', 500, '-'), ('CD19', 200, '+')],\n    'NK cells': [('CD3', 500, '-'), ('CD56', 200, '+')],\n}\n\n# Calculate (assuming live-gated events)\nresults = immunophenotype(events, channel_names, phenotype_gates, parent_mask=singlet_gate)\nfor pop, data in results.items():\n    print(f\"{pop}: {data['count']} cells ({data['frequency']:.1f}%)\")\n```\n\n### 5. CFSE Proliferation Analysis\n\nQuantify cell division from dye dilution.\n\n```python\nimport numpy as np\nfrom scipy.signal import find_peaks\nfrom scipy.optimize import curve_fit\n\ndef analyze_cfse_proliferation(cfse_values, n_generations=8):\n    \"\"\"Analyze CFSE dilution to quantify proliferation.\"\"\"\n    # Log-transform CFSE values\n    log_cfse = np.log2(cfse_values[cfse_values > 0])\n\n    # Build histogram\n    hist, bin_edges = np.histogram(log_cfse, bins=200)\n    bin_centers = (bin_edges[:-1] + bin_edges[1:]) \u002F 2\n\n    # Find peaks (each peak = one generation)\n    peaks, properties = find_peaks(hist, height=len(cfse_values)*0.005,\n                                    distance=10, prominence=50)\n\n    # Peaks should be ~1 log2 unit apart (halving of dye)\n    peak_positions = bin_centers[peaks]\n    peak_heights = hist[peaks]\n\n    # Calculate division index\n    # DI = sum(cells in gen i) \u002F sum(cells in gen 0 equivalents)\n    total_cells = sum(peak_heights)\n    undivided_equiv = sum(h \u002F (2**i) for i, h in enumerate(peak_heights))\n    division_index = total_cells \u002F undivided_equiv if undivided_equiv > 0 else 0\n\n    # Proliferation index (only among divided cells)\n    if len(peak_heights) > 1:\n        divided_cells = sum(peak_heights[1:])\n        divided_precursors = sum(h \u002F (2**i) for i, h in enumerate(peak_heights[1:], 1))\n        prolif_index = divided_cells \u002F divided_precursors if divided_precursors > 0 else 0\n    else:\n        prolif_index = 1.0\n\n    return {\n        'n_peaks': len(peaks),\n        'division_index': division_index,\n        'proliferation_index': prolif_index,\n        'peak_positions': peak_positions,\n        'peak_heights': peak_heights\n    }\n\n# Analyze\ncfse_idx = channel_names.index('CFSE') if 'CFSE' in channel_names else 0\ncfse_data = events[singlet_gate, cfse_idx]\nprolif = analyze_cfse_proliferation(cfse_data)\nprint(f\"Generations detected: {prolif['n_peaks']}\")\nprint(f\"Division index: {prolif['division_index']:.2f}\")\nprint(f\"Proliferation index: {prolif['proliferation_index']:.2f}\")\n```\n\n### 6. Cell Cycle Analysis\n\nFit DNA content histograms to determine cell cycle phase distribution.\n\n```python\nimport numpy as np\nfrom scipy.optimize import curve_fit\n\ndef dean_jett_fox(x, g1_mean, g1_std, g1_amp,\n                  s_amp, s_slope,\n                  g2_amp, g2_std):\n    \"\"\"Dean-Jett-Fox cell cycle model.\n    G1 and G2\u002FM as Gaussians, S-phase as polynomial.\"\"\"\n    g2_mean = 2 * g1_mean  # G2\u002FM DNA content is 2x G1\n\n    # G1 peak\n    g1 = g1_amp * np.exp(-0.5 * ((x - g1_mean) \u002F g1_std) ** 2)\n\n    # G2\u002FM peak\n    g2 = g2_amp * np.exp(-0.5 * ((x - g2_mean) \u002F g2_std) ** 2)\n\n    # S-phase (linear between G1 and G2)\n    s = np.zeros_like(x)\n    s_mask = (x > g1_mean + g1_std) & (x \u003C g2_mean - g2_std)\n    s[s_mask] = s_amp + s_slope * (x[s_mask] - g1_mean)\n\n    return g1 + g2 + np.clip(s, 0, None)\n\ndef cell_cycle_analysis(dna_values):\n    \"\"\"Determine G0\u002FG1, S, G2\u002FM percentages from PI staining.\"\"\"\n    hist, bin_edges = np.histogram(dna_values, bins=256, range=(0, dna_values.max()))\n    bin_centers = (bin_edges[:-1] + bin_edges[1:]) \u002F 2\n\n    # Initial parameter estimates\n    g1_peak = bin_centers[np.argmax(hist)]\n    g1_amp = hist.max()\n\n    p0 = [g1_peak, g1_peak*0.05, g1_amp, g1_amp*0.1, 0, g1_amp*0.3, g1_peak*0.07]\n    bounds = ([0, 0, 0, 0, -np.inf, 0, 0],\n              [np.inf, np.inf, np.inf, np.inf, np.inf, np.inf, np.inf])\n\n    try:\n        popt, pcov = curve_fit(dean_jett_fox, bin_centers, hist, p0=p0,\n                                bounds=bounds, maxfev=10000)\n        fitted = dean_jett_fox(bin_centers, *popt)\n\n        # Calculate phase fractions\n        g1_mean, g1_std = popt[0], popt[1]\n        g2_mean = 2 * g1_mean\n\n        g1_mask = bin_centers \u003C (g1_mean + 2*g1_std)\n        g2_mask = bin_centers > (g2_mean - 2*popt[5])\n        s_mask = ~g1_mask & ~g2_mask\n\n        total = hist.sum()\n        g1_pct = 100 * hist[g1_mask].sum() \u002F total\n        s_pct = 100 * hist[s_mask].sum() \u002F total\n        g2m_pct = 100 * hist[g2_mask].sum() \u002F total\n\n        return {'G0\u002FG1': g1_pct, 'S': s_pct, 'G2\u002FM': g2m_pct}\n    except RuntimeError:\n        return {'error': 'Curve fitting failed — check DNA histogram quality'}\n\n# Analyze\npi_idx = channel_names.index('PI') if 'PI' in channel_names else 0\ndna_data = events[singlet_gate, pi_idx]\nphases = cell_cycle_analysis(dna_data)\nprint(f\"G0\u002FG1: {phases.get('G0\u002FG1', 'N\u002FA'):.1f}%\")\nprint(f\"S: {phases.get('S', 'N\u002FA'):.1f}%\")\nprint(f\"G2\u002FM: {phases.get('G2\u002FM', 'N\u002FA'):.1f}%\")\n```\n\n### 7. Apoptosis Assays\n\nAnalyze Annexin V \u002F Propidium Iodide staining.\n\n```python\nimport numpy as np\n\ndef annexin_v_pi_analysis(events, annexin_idx, pi_idx,\n                          annexin_threshold, pi_threshold, parent_mask=None):\n    \"\"\"Quadrant analysis for apoptosis assay.\"\"\"\n    if parent_mask is None:\n        parent_mask = np.ones(len(events), dtype=bool)\n\n    gated = events[parent_mask]\n    total = len(gated)\n\n    annexin_pos = gated[:, annexin_idx] > annexin_threshold\n    pi_pos = gated[:, pi_idx] > pi_threshold\n\n    viable = (~annexin_pos) & (~pi_pos)\n    early_apoptotic = annexin_pos & (~pi_pos)\n    late_apoptotic = annexin_pos & pi_pos\n    necrotic = (~annexin_pos) & pi_pos\n\n    return {\n        'viable': 100 * viable.sum() \u002F total,\n        'early_apoptotic': 100 * early_apoptotic.sum() \u002F total,\n        'late_apoptotic': 100 * late_apoptotic.sum() \u002F total,\n        'necrotic': 100 * necrotic.sum() \u002F total\n    }\n\n# Analyze\nresults = annexin_v_pi_analysis(events, annexin_idx=3, pi_idx=4,\n                                 annexin_threshold=200, pi_threshold=200,\n                                 parent_mask=singlet_gate)\nfor state, pct in results.items():\n    print(f\"{state}: {pct:.1f}%\")\n```\n\n### 8. Visualization\n\nCreate publication-quality flow cytometry plots.\n\n```python\nimport matplotlib.pyplot as plt\nimport numpy as np\n\ndef density_plot(events, x_idx, y_idx, channel_names, ax=None, gate_mask=None):\n    \"\"\"2D density (pseudo-color) plot.\"\"\"\n    if ax is None:\n        fig, ax = plt.subplots(figsize=(6, 6))\n    data = events if gate_mask is None else events[gate_mask]\n    ax.hist2d(data[:, x_idx], data[:, y_idx], bins=256,\n              cmap='jet', norm=plt.matplotlib.colors.LogNorm())\n    ax.set_xlabel(channel_names[x_idx])\n    ax.set_ylabel(channel_names[y_idx])\n    return ax\n\ndef histogram_overlay(datasets, channel_idx, labels, channel_name, ax=None):\n    \"\"\"Overlay histograms for comparing conditions.\"\"\"\n    if ax is None:\n        fig, ax = plt.subplots(figsize=(8, 5))\n    for data, label in zip(datasets, labels):\n        ax.hist(data[:, channel_idx], bins=256, alpha=0.5, label=label, density=True)\n    ax.set_xlabel(channel_name)\n    ax.set_ylabel('Density')\n    ax.legend()\n    return ax\n```\n\n## Typical Workflows\n\n### Workflow 1: Full Immunophenotyping from Multi-Color FCS Files\n\n```python\nfrom flowio import FlowData\nimport numpy as np\n\nfcs = FlowData('pbmc_panel.fcs')\nevents = fcs.as_array()\nch = fcs.pnn_labels\n\n# Compensate\nspill_str = fcs.text.get('$SPILLOVER', fcs.text.get('SPILL', ''))\nif spill_str:\n    comp_ch, spill = parse_spillover_matrix(spill_str)\n    fidx = [ch.index(c) for c in comp_ch]\n    events = compensate(events, fidx, spill)\n\n# Gate: debris → singlets → live\nfsc, ssc = ch.index('FSC-A'), ch.index('SSC-A')\ngate = (events[:, fsc] > 30000) & (events[:, ssc] > 5000) & (events[:, ssc] \u003C 200000)\n\n# Immunophenotype\npanels = {\n    'T cells (CD3+)': [('CD3', 500, '+')],\n    'Helper T (CD3+CD4+)': [('CD3', 500, '+'), ('CD4', 300, '+')],\n    'Cytotoxic T (CD3+CD8+)': [('CD3', 500, '+'), ('CD8', 300, '+')],\n}\nresults = immunophenotype(events, ch, panels, parent_mask=gate)\nfor pop, data in results.items():\n    print(f\"{pop}: {data['frequency']:.1f}%\")\n```\n\n### Workflow 2: CFSE Proliferation Analysis with Division Tracking\n\n```python\nfrom flowio import FlowData\n\nfcs = FlowData('cfse_stimulated.fcs')\nevents = fcs.as_array()\nch = fcs.pnn_labels\n\n# Gate live cells\nfsc = ch.index('FSC-A')\nlive = events[:, fsc] > 20000\n\n# CFSE analysis\ncfse_idx = ch.index('CFSE')\nprolif = analyze_cfse_proliferation(events[live, cfse_idx])\nprint(f\"Divisions detected: {prolif['n_peaks']}\")\nprint(f\"Division index: {prolif['division_index']:.2f}\")\nprint(f\"Proliferation index: {prolif['proliferation_index']:.2f}\")\n```\n\n### Workflow 3: Cell Cycle Distribution from PI-Stained Samples\n\n```python\nfrom flowio import FlowData\n\nfcs = FlowData('pi_stained.fcs')\nevents = fcs.as_array()\nch = fcs.pnn_labels\n\n# Gate singlets (PI-area vs PI-width)\npi_a = ch.index('PI-A')\npi_w = ch.index('PI-W') if 'PI-W' in ch else None\n\nif pi_w:\n    singlets = (events[:, pi_w] > 50000) & (events[:, pi_w] \u003C 150000)\nelse:\n    singlets = np.ones(len(events), dtype=bool)\n\nphases = cell_cycle_analysis(events[singlets, pi_a])\nfor phase, pct in phases.items():\n    print(f\"{phase}: {pct:.1f}%\")\n```\n\n## Best Practices\n\n1. **Always compensate** before gating on fluorescence channels — uncompensated data leads to incorrect population identification\n2. **Gate sequentially** — debris exclusion first, then singlets, then viability, then markers\n3. **Use FMO controls** (Fluorescence Minus One) to set accurate positive\u002Fnegative thresholds\n4. **Log or biexponential transform** fluorescence data for visualization and gating\n5. **Back-gate** to verify gated populations appear in expected scatter positions\n6. **Include isotype controls** or unstained controls for threshold determination\n7. **Report parent gate denominators** — frequencies must reference the correct parent population\n8. **Quality check** — verify event counts are sufficient for rare populations (>100 events minimum)\n\n## Troubleshooting\n\n**Problem:** Compensation produces negative values\n**Solution:** This is normal for properly compensated data. Do not zero-clip — negative values carry information. Use biexponential display for visualization.\n\n**Problem:** GMM auto-gating splits one population into two\n**Solution:** Reduce `n_components`. Pre-gate to remove obvious debris first. Try log-transforming data before fitting.\n\n**Problem:** Cell cycle fitting fails\n**Solution:** Ensure DNA histogram has clear G1 peak. Filter for singlets using DNA-area vs DNA-width. Check that PI staining is optimal (no under\u002Fover-staining).\n\n**Problem:** CFSE peaks not resolved\n**Solution:** Increase histogram bins. Ensure cells were labeled at correct CFSE concentration. Later generations may be unresolvable — focus on first 4-5 divisions.\n\n**Problem:** FCS file channels have unexpected names\n**Solution:** Check both `pnn_labels` (short names) and `pns_labels` (descriptive stain names). Different instruments use different naming conventions.\n\n## Resources\n\n- [FlowIO Documentation](https:\u002F\u002Fgithub.com\u002Fwhitews\u002FFlowIO)\n- [ISAC Data Standards](https:\u002F\u002Fisac-net.org\u002Fpage\u002Fdata-standards)\n- [Flow Cytometry Bioinformatics (Bioconductor)](https:\u002F\u002Fwww.bioconductor.org\u002Fpackages\u002Frelease\u002FBiocViews.html#___FlowCytometry)\n- [Practical Flow Cytometry by Howard Shapiro](https:\u002F\u002Fonlinelibrary.wiley.com\u002Fdoi\u002Fbook\u002F10.1002\u002F0471722731)\n","# Flow Cytometry Analysis: Complete Analysis Pipeline\n\n## Overview\n\nFlow Cytometry Analysis provides end-to-end computational workflows for analyzing flow cytometry data. Starting from FCS file parsing, through compensation matrix application, gating strategies (manual rectangular\u002Fpolygon gates and automated Gaussian mixture model gating), to downstream analyses including immunophenotyping, CFSE proliferation tracking, cell cycle phase quantification (Dean-Jett-Fox model), and apoptosis assays (Annexin V\u002FPI). This skill extends basic FCS file handling (flowio) with full analytical pipelines.\n\n## When to Use This Skill\n\n- Analyzing multi-color flow cytometry experiments\n- Applying compensation matrices to correct spectral overlap\n- Building sequential gating hierarchies (debris exclusion, singlet gating, live\u002Fdead, marker gating)\n- Immunophenotyping with multi-marker panels (CD3, CD4, CD8, etc.)\n- Quantifying cell proliferation from CFSE\u002FCellTrace dilution\n- Determining cell cycle phase distribution from DNA content histograms\n- Analyzing apoptosis from Annexin V \u002F propidium iodide staining\n- Creating density plots, histograms, and overlay visualizations\n- Automated gating for high-throughput cytometry experiments\n\n**Related Skills:** For raw FCS file parsing and creation use `flowio`. For single-cell RNA-seq analysis use `scanpy`.\n\n## Installation\n\n```bash\nuv pip install flowio fcsparser scipy numpy pandas matplotlib scikit-learn\n```\n\n## Quick Start\n\n```python\nfrom flowio import FlowData\nimport numpy as np\n\n# Read FCS file\nfcs = FlowData('sample.fcs')\nevents = fcs.as_array()\nchannels = fcs.pnn_labels\n\nprint(f\"Events: {events.shape[0]}, Channels: {len(channels)}\")\nprint(f\"Channels: {channels}\")\n\n# Simple FSC\u002FSSC gate (remove debris)\nfsc_idx = channels.index('FSC-A')\nssc_idx = channels.index('SSC-A')\nmask = (events[:, fsc_idx] > 50000) & (events[:, ssc_idx] > 10000)\ngated = events[mask]\nprint(f\"After gating: {gated.shape[0]} events ({100*mask.sum()\u002Flen(mask):.1f}%)\")\n```\n\n## Core Capabilities\n\n### 1. FCS File Handling\n\nRead FCS files and extract channel information.\n\n```python\nfrom flowio import FlowData\nimport fcsparser\nimport numpy as np\n\n# Method 1: flowio\nfcs = FlowData('sample.fcs')\nevents = fcs.as_array()\nchannel_names = fcs.pnn_labels\nstain_names = fcs.pns_labels\n\n# Method 2: fcsparser (returns metadata + DataFrame)\nmeta, data = fcsparser.parse('sample.fcs', reformat_meta=True)\nprint(f\"Channels: {list(data.columns)}\")\n\n# Extract compensation matrix from metadata\nif '$SPILLOVER' in fcs.text or 'SPILL' in fcs.text:\n    spill_str = fcs.text.get('$SPILLOVER', fcs.text.get('SPILL', ''))\n    print(\"Compensation matrix found in metadata\")\n```\n\n### 2. Compensation\n\nApply spectral overlap correction.\n\n```python\nimport numpy as np\n\ndef parse_spillover_matrix(spill_string):\n    \"\"\"Parse $SPILLOVER or SPILL keyword from FCS metadata.\"\"\"\n    parts = spill_string.split(',')\n    n = int(parts[0])\n    channel_names = parts[1:n+1]\n    values = [float(x) for x in parts[n+1:]]\n    matrix = np.array(values).reshape(n, n)\n    return channel_names, matrix\n\ndef compensate(events, fluoro_indices, spillover_matrix):\n    \"\"\"Apply compensation to fluorescence channels.\"\"\"\n    inv_spill = np.linalg.inv(spillover_matrix)\n    compensated = events.copy()\n    fluoro_data = events[:, fluoro_indices]\n    compensated[:, fluoro_indices] = fluoro_data @ inv_spill.T\n    return compensated\n\n# Apply compensation\nspill_str = fcs.text.get('$SPILLOVER', fcs.text.get('SPILL', ''))\nif spill_str:\n    comp_channels, spill_matrix = parse_spillover_matrix(spill_str)\n    fluoro_idx = [channel_names.index(ch) for ch in comp_channels]\n    events_comp = compensate(events, fluoro_idx, spill_matrix)\n    print(f\"Compensated {len(fluoro_idx)} fluorescence channels\")\n```\n\n### 3. Gating Strategies\n\nApply sequential gates to identify cell populations.\n\n**Manual rectangular gates:**\n\n```python\nimport numpy as np\n\ndef rect_gate(events, ch_idx, lo, hi):\n    \"\"\"Apply rectangular gate on one channel.\"\"\"\n    return (events[:, ch_idx] >= lo) & (events[:, ch_idx] \u003C= hi)\n\ndef polygon_gate(events, x_idx, y_idx, vertices):\n    \"\"\"Apply polygon gate using ray casting.\"\"\"\n    from matplotlib.path import Path\n    points = np.column_stack([events[:, x_idx], events[:, y_idx]])\n    poly = Path(vertices)\n    return poly.contains_points(points)\n\n# Sequential gating tree\n# Step 1: FSC\u002FSSC - remove debris\nscatter_gate = rect_gate(events, fsc_idx, 30000, 250000) & \\\n               rect_gate(events, ssc_idx, 5000, 200000)\n\n# Step 2: Singlet gate (FSC-A vs FSC-H)\nfsch_idx = channel_names.index('FSC-H')\nsinglet_gate = scatter_gate.copy()\nratio = events[:, fsc_idx] \u002F (events[:, fsch_idx] + 1)\nsinglet_gate &= (ratio > 0.8) & (ratio \u003C 1.2)\n\n# Step 3: Live gate (exclude viability dye positive)\n# live_gate = singlet_gate & rect_gate(events, viability_idx, 0, threshold)\n\nprint(f\"Debris exclusion: {scatter_gate.sum()} events\")\nprint(f\"Singlets: {singlet_gate.sum()} events\")\n```\n\n**Automated gating with Gaussian Mixture Models:**\n\n```python\nfrom sklearn.mixture import GaussianMixture\nimport numpy as np\n\ndef auto_gate_gmm(events, channels_idx, n_components=2):\n    \"\"\"Automated gating using Gaussian Mixture Model.\"\"\"\n    data = events[:, channels_idx]\n    # Log transform for better separation\n    data_log = np.log1p(np.clip(data, 0, None))\n\n    gmm = GaussianMixture(n_components=n_components, random_state=42)\n    labels = gmm.fit_predict(data_log)\n\n    # Identify populations by mean intensity\n    means = gmm.means_\n    pop_order = np.argsort(means.sum(axis=1))\n\n    return labels, gmm, pop_order\n\n# Auto-gate lymphocytes vs debris\nlabels, gmm, order = auto_gate_gmm(events, [fsc_idx, ssc_idx], n_components=3)\nlymphocyte_mask = labels == order[1]  # Middle population is typically lymphocytes\nprint(f\"Lymphocyte gate: {lymphocyte_mask.sum()} events\")\n```\n\n### 4. Immunophenotyping\n\nMulti-marker panel analysis for population identification.\n\n```python\nimport numpy as np\nimport pandas as pd\n\ndef immunophenotype(events, channel_names, gates, parent_mask=None):\n    \"\"\"Calculate population frequencies from marker gates.\"\"\"\n    if parent_mask is None:\n        parent_mask = np.ones(len(events), dtype=bool)\n\n    results = {}\n    parent_count = parent_mask.sum()\n\n    for pop_name, marker_gates in gates.items():\n        pop_mask = parent_mask.copy()\n        for ch_name, threshold, direction in marker_gates:\n            ch_idx = channel_names.index(ch_name)\n            if direction == '+':\n                pop_mask &= events[:, ch_idx] > threshold\n            else:\n                pop_mask &= events[:, ch_idx] \u003C= threshold\n\n        count = pop_mask.sum()\n        freq = 100 * count \u002F parent_count if parent_count > 0 else 0\n        results[pop_name] = {'count': count, 'frequency': freq, 'mask': pop_mask}\n\n    return results\n\n# Define immunophenotyping panel\nphenotype_gates = {\n    'CD3+ T cells': [('CD3', 500, '+')],\n    'CD4+ T helper': [('CD3', 500, '+'), ('CD4', 300, '+'), ('CD8', 300, '-')],\n    'CD8+ T cytotoxic': [('CD3', 500, '+'), ('CD4', 300, '-'), ('CD8', 300, '+')],\n    'B cells (CD19+)': [('CD3', 500, '-'), ('CD19', 200, '+')],\n    'NK cells': [('CD3', 500, '-'), ('CD56', 200, '+')],\n}\n\n# Calculate (assuming live-gated events)\nresults = immunophenotype(events, channel_names, phenotype_gates, parent_mask=singlet_gate)\nfor pop, data in results.items():\n    print(f\"{pop}: {data['count']} cells ({data['frequency']:.1f}%)\")\n```\n\n### 5. CFSE Proliferation Analysis\n\nQuantify cell division from dye dilution.\n\n```python\nimport numpy as np\nfrom scipy.signal import find_peaks\nfrom scipy.optimize import curve_fit\n\ndef analyze_cfse_proliferation(cfse_values, n_generations=8):\n    \"\"\"Analyze CFSE dilution to quantify proliferation.\"\"\"\n    # Log-transform CFSE values\n    log_cfse = np.log2(cfse_values[cfse_values > 0])\n\n    # Build histogram\n    hist, bin_edges = np.histogram(log_cfse, bins=200)\n    bin_centers = (bin_edges[:-1] + bin_edges[1:]) \u002F 2\n\n    # Find peaks (each peak = one generation)\n    peaks, properties = find_peaks(hist, height=len(cfse_values)*0.005,\n                                    distance=10, prominence=50)\n\n    # Peaks should be ~1 log2 unit apart (halving of dye)\n    peak_positions = bin_centers[peaks]\n    peak_heights = hist[peaks]\n\n    # Calculate division index\n    # DI = sum(cells in gen i) \u002F sum(cells in gen 0 equivalents)\n    total_cells = sum(peak_heights)\n    undivided_equiv = sum(h \u002F (2**i) for i, h in enumerate(peak_heights))\n    division_index = total_cells \u002F undivided_equiv if undivided_equiv > 0 else 0\n\n    # Proliferation index (only among divided cells)\n    if len(peak_heights) > 1:\n        divided_cells = sum(peak_heights[1:])\n        divided_precursors = sum(h \u002F (2**i) for i, h in enumerate(peak_heights[1:], 1))\n        prolif_index = divided_cells \u002F divided_precursors if divided_precursors > 0 else 0\n    else:\n        prolif_index = 1.0\n\n    return {\n        'n_peaks': len(peaks),\n        'division_index': division_index,\n        'proliferation_index': prolif_index,\n        'peak_positions': peak_positions,\n        'peak_heights': peak_heights\n    }\n\n# Analyze\ncfse_idx = channel_names.index('CFSE') if 'CFSE' in channel_names else 0\ncfse_data = events[singlet_gate, cfse_idx]\nprolif = analyze_cfse_proliferation(cfse_data)\nprint(f\"Generations detected: {prolif['n_peaks']}\")\nprint(f\"Division index: {prolif['division_index']:.2f}\")\nprint(f\"Proliferation index: {prolif['proliferation_index']:.2f}\")\n```\n\n### 6. Cell Cycle Analysis\n\nFit DNA content histograms to determine cell cycle phase distribution.\n\n```python\nimport numpy as np\nfrom scipy.optimize import curve_fit\n\ndef dean_jett_fox(x, g1_mean, g1_std, g1_amp,\n                  s_amp, s_slope,\n                  g2_amp, g2_std):\n    \"\"\"Dean-Jett-Fox cell cycle model.\n    G1 and G2\u002FM as Gaussians, S-phase as polynomial.\"\"\"\n    g2_mean = 2 * g1_mean  # G2\u002FM DNA content is 2x G1\n\n    # G1 peak\n    g1 = g1_amp * np.exp(-0.5 * ((x - g1_mean) \u002F g1_std) ** 2)\n\n    # G2\u002FM peak\n    g2 = g2_amp * np.exp(-0.5 * ((x - g2_mean) \u002F g2_std) ** 2)\n\n    # S-phase (linear between G1 and G2)\n    s = np.zeros_like(x)\n    s_mask = (x > g1_mean + g1_std) & (x \u003C g2_mean - g2_std)\n    s[s_mask] = s_amp + s_slope * (x[s_mask] - g1_mean)\n\n    return g1 + g2 + np.clip(s, 0, None)\n\ndef cell_cycle_analysis(dna_values):\n    \"\"\"Determine G0\u002FG1, S, G2\u002FM percentages from PI staining.\"\"\"\n    hist, bin_edges = np.histogram(dna_values, bins=256, range=(0, dna_values.max()))\n    bin_centers = (bin_edges[:-1] + bin_edges[1:]) \u002F 2\n\n    # Initial parameter estimates\n    g1_peak = bin_centers[np.argmax(hist)]\n    g1_amp = hist.max()\n\n    p0 = [g1_peak, g1_peak*0.05, g1_amp, g1_amp*0.1, 0, g1_amp*0.3, g1_peak*0.07]\n    bounds = ([0, 0, 0, 0, -np.inf, 0, 0],\n              [np.inf, np.inf, np.inf, np.inf, np.inf, np.inf, np.inf])\n\n    try:\n        popt, pcov = curve_fit(dean_jett_fox, bin_centers, hist, p0=p0,\n                                bounds=bounds, maxfev=10000)\n        fitted = dean_jett_fox(bin_centers, *popt)\n\n        # Calculate phase fractions\n        g1_mean, g1_std = popt[0], popt[1]\n        g2_mean = 2 * g1_mean\n\n        g1_mask = bin_centers \u003C (g1_mean + 2*g1_std)\n        g2_mask = bin_centers > (g2_mean - 2*popt[5])\n        s_mask = ~g1_mask & ~g2_mask\n\n        total = hist.sum()\n        g1_pct = 100 * hist[g1_mask].sum() \u002F total\n        s_pct = 100 * hist[s_mask].sum() \u002F total\n        g2m_pct = 100 * hist[g2_mask].sum() \u002F total\n\n        return {'G0\u002FG1': g1_pct, 'S': s_pct, 'G2\u002FM': g2m_pct}\n    except RuntimeError:\n        return {'error': 'Curve fitting failed — check DNA histogram quality'}\n\n# Analyze\npi_idx = channel_names.index('PI') if 'PI' in channel_names else 0\ndna_data = events[singlet_gate, pi_idx]\nphases = cell_cycle_analysis(dna_data)\nprint(f\"G0\u002FG1: {phases.get('G0\u002FG1', 'N\u002FA'):.1f}%\")\nprint(f\"S: {phases.get('S', 'N\u002FA'):.1f}%\")\nprint(f\"G2\u002FM: {phases.get('G2\u002FM', 'N\u002FA'):.1f}%\")\n```\n\n### 7. Apoptosis Assays\n\nAnalyze Annexin V \u002F Propidium Iodide staining.\n\n```python\nimport numpy as np\n\ndef annexin_v_pi_analysis(events, annexin_idx, pi_idx,\n                          annexin_threshold, pi_threshold, parent_mask=None):\n    \"\"\"Quadrant analysis for apoptosis assay.\"\"\"\n    if parent_mask is None:\n        parent_mask = np.ones(len(events), dtype=bool)\n\n    gated = events[parent_mask]\n    total = len(gated)\n\n    annexin_pos = gated[:, annexin_idx] > annexin_threshold\n    pi_pos = gated[:, pi_idx] > pi_threshold\n\n    viable = (~annexin_pos) & (~pi_pos)\n    early_apoptotic = annexin_pos & (~pi_pos)\n    late_apoptotic = annexin_pos & pi_pos\n    necrotic = (~annexin_pos) & pi_pos\n\n    return {\n        'viable': 100 * viable.sum() \u002F total,\n        'early_apoptotic': 100 * early_apoptotic.sum() \u002F total,\n        'late_apoptotic': 100 * late_apoptotic.sum() \u002F total,\n        'necrotic': 100 * necrotic.sum() \u002F total\n    }\n\n# Analyze\nresults = annexin_v_pi_analysis(events, annexin_idx=3, pi_idx=4,\n                                 annexin_threshold=200, pi_threshold=200,\n                                 parent_mask=singlet_gate)\nfor state, pct in results.items():\n    print(f\"{state}: {pct:.1f}%\")\n```\n\n### 8. Visualization\n\nCreate publication-quality flow cytometry plots.\n\n```python\nimport matplotlib.pyplot as plt\nimport numpy as np\n\ndef density_plot(events, x_idx, y_idx, channel_names, ax=None, gate_mask=None):\n    \"\"\"2D density (pseudo-color) plot.\"\"\"\n    if ax is None:\n        fig, ax = plt.subplots(figsize=(6, 6))\n    data = events if gate_mask is None else events[gate_mask]\n    ax.hist2d(data[:, x_idx], data[:, y_idx], bins=256,\n              cmap='jet', norm=plt.matplotlib.colors.LogNorm())\n    ax.set_xlabel(channel_names[x_idx])\n    ax.set_ylabel(channel_names[y_idx])\n    return ax\n\ndef histogram_overlay(datasets, channel_idx, labels, channel_name, ax=None):\n    \"\"\"Overlay histograms for comparing conditions.\"\"\"\n    if ax is None:\n        fig, ax = plt.subplots(figsize=(8, 5))\n    for data, label in zip(datasets, labels):\n        ax.hist(data[:, channel_idx], bins=256, alpha=0.5, label=label, density=True)\n    ax.set_xlabel(channel_name)\n    ax.set_ylabel('Density')\n    ax.legend()\n    return ax\n```\n\n## Typical Workflows\n\n### Workflow 1: Full Immunophenotyping from Multi-Color FCS Files\n\n```python\nfrom flowio import FlowData\nimport numpy as np\n\nfcs = FlowData('pbmc_panel.fcs')\nevents = fcs.as_array()\nch = fcs.pnn_labels\n\n# Compensate\nspill_str = fcs.text.get('$SPILLOVER', fcs.text.get('SPILL', ''))\nif spill_str:\n    comp_ch, spill = parse_spillover_matrix(spill_str)\n    fidx = [ch.index(c) for c in comp_ch]\n    events = compensate(events, fidx, spill)\n\n# Gate: debris → singlets → live\nfsc, ssc = ch.index('FSC-A'), ch.index('SSC-A')\ngate = (events[:, fsc] > 30000) & (events[:, ssc] > 5000) & (events[:, ssc] \u003C 200000)\n\n# Immunophenotype\npanels = {\n    'T cells (CD3+)': [('CD3', 500, '+')],\n    'Helper T (CD3+CD4+)': [('CD3', 500, '+'), ('CD4', 300, '+')],\n    'Cytotoxic T (CD3+CD8+)': [('CD3', 500, '+'), ('CD8', 300, '+')],\n}\nresults = immunophenotype(events, ch, panels, parent_mask=gate)\nfor pop, data in results.items():\n    print(f\"{pop}: {data['frequency']:.1f}%\")\n```\n\n### Workflow 2: CFSE Proliferation Analysis with Division Tracking\n\n```python\nfrom flowio import FlowData\n\nfcs = FlowData('cfse_stimulated.fcs')\nevents = fcs.as_array()\nch = fcs.pnn_labels\n\n# Gate live cells\nfsc = ch.index('FSC-A')\nlive = events[:, fsc] > 20000\n\n# CFSE analysis\ncfse_idx = ch.index('CFSE')\nprolif = analyze_cfse_proliferation(events[live, cfse_idx])\nprint(f\"Divisions detected: {prolif['n_peaks']}\")\nprint(f\"Division index: {prolif['division_index']:.2f}\")\nprint(f\"Proliferation index: {prolif['proliferation_index']:.2f}\")\n```\n\n### Workflow 3: Cell Cycle Distribution from PI-Stained Samples\n\n```python\nfrom flowio import FlowData\n\nfcs = FlowData('pi_stained.fcs')\nevents = fcs.as_array()\nch = fcs.pnn_labels\n\n# Gate singlets (PI-area vs PI-width)\npi_a = ch.index('PI-A')\npi_w = ch.index('PI-W') if 'PI-W' in ch else None\n\nif pi_w:\n    singlets = (events[:, pi_w] > 50000) & (events[:, pi_w] \u003C 150000)\nelse:\n    singlets = np.ones(len(events), dtype=bool)\n\nphases = cell_cycle_analysis(events[singlets, pi_a])\nfor phase, pct in phases.items():\n    print(f\"{phase}: {pct:.1f}%\")\n```\n\n## Best Practices\n\n1. **Always compensate** before gating on fluorescence channels — uncompensated data leads to incorrect population identification\n2. **Gate sequentially** — debris exclusion first, then singlets, then viability, then markers\n3. **Use FMO controls** (Fluorescence Minus One) to set accurate positive\u002Fnegative thresholds\n4. **Log or biexponential transform** fluorescence data for visualization and gating\n5. **Back-gate** to verify gated populations appear in expected scatter positions\n6. **Include isotype controls** or unstained controls for threshold determination\n7. **Report parent gate denominators** — frequencies must reference the correct parent population\n8. **Quality check** — verify event counts are sufficient for rare populations (>100 events minimum)\n\n## Troubleshooting\n\n**Problem:** Compensation produces negative values\n**Solution:** This is normal for properly compensated data. Do not zero-clip — negative values carry information. Use biexponential display for visualization.\n\n**Problem:** GMM auto-gating splits one population into two\n**Solution:** Reduce `n_components`. Pre-gate to remove obvious debris first. Try log-transforming data before fitting.\n\n**Problem:** Cell cycle fitting fails\n**Solution:** Ensure DNA histogram has clear G1 peak. Filter for singlets using DNA-area vs DNA-width. Check that PI staining is optimal (no under\u002Fover-staining).\n\n**Problem:** CFSE peaks not resolved\n**Solution:** Increase histogram bins. Ensure cells were labeled at correct CFSE concentration. Later generations may be unresolvable — focus on first 4-5 divisions.\n\n**Problem:** FCS file channels have unexpected names\n**Solution:** Check both `pnn_labels` (short names) and `pns_labels` (descriptive stain names). Different instruments use different naming conventions.\n\n## Resources\n\n- [FlowIO Documentation](https:\u002F\u002Fgithub.com\u002Fwhitews\u002FFlowIO)\n- [ISAC Data Standards](https:\u002F\u002Fisac-net.org\u002Fpage\u002Fdata-standards)\n- [Flow Cytometry Bioinformatics (Bioconductor)](https:\u002F\u002Fwww.bioconductor.org\u002Fpackages\u002Frelease\u002FBiocViews.html#___FlowCytometry)\n- [Practical Flow Cytometry by Howard Shapiro](https:\u002F\u002Fonlinelibrary.wiley.com\u002Fdoi\u002Fbook\u002F10.1002\u002F0471722731)\n",{"name":5,"license":44,"category":28,"metadata":45,"description":7},"MIT license",{"skill-author":46},"InkVell Inc.","main","4aa39efd1cdab0b4657ecee16c92a92011a5b10f","openscience\u002Fbackend\u002Fcli\u002Fskills\u002Fbiology\u002Fflow-cytometry-analysis","https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub","https:\u002F\u002Fgithub.com\u002Ffadrienne","2026-10-28 00:43:09",{"source":8,"skillName":5,"commands":54,"prompt":64,"zipAvailable":65},[55,58,61],{"tool":56,"command":57},"npx","npx skills add fadrienne\u002FMyGitHub --skill flow-cytometry-analysis",{"tool":59,"command":60},"bunx","bunx skills add fadrienne\u002FMyGitHub --skill flow-cytometry-analysis",{"tool":62,"command":63},"pnpm","pnpm dlx skills add fadrienne\u002FMyGitHub --skill flow-cytometry-analysis","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002Fopenscience\u002Fbackend\u002Fcli\u002Fskills\u002Fbiology\u002Fflow-cytometry-analysis\u002FSKILL.md",true,[67,73,77,81],{"path":68,"resourceType":69,"resourceTypeDesc":70,"sizeBytes":71,"sourceHtmlUrl":72},"scripts\u002Fcell_cycle.py","SCRIPT","脚本",15854,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002Fopenscience\u002Fbackend\u002Fcli\u002Fskills\u002Fbiology\u002Fflow-cytometry-analysis\u002Fscripts\u002Fcell_cycle.py",{"path":74,"resourceType":69,"resourceTypeDesc":70,"sizeBytes":75,"sourceHtmlUrl":76},"scripts\u002Fcfse_proliferation.py",18794,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002Fopenscience\u002Fbackend\u002Fcli\u002Fskills\u002Fbiology\u002Fflow-cytometry-analysis\u002Fscripts\u002Fcfse_proliferation.py",{"path":78,"resourceType":69,"resourceTypeDesc":70,"sizeBytes":79,"sourceHtmlUrl":80},"scripts\u002Fgate_fcs.py",11764,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002Fopenscience\u002Fbackend\u002Fcli\u002Fskills\u002Fbiology\u002Fflow-cytometry-analysis\u002Fscripts\u002Fgate_fcs.py",{"path":82,"resourceType":69,"resourceTypeDesc":70,"sizeBytes":83,"sourceHtmlUrl":84},"scripts\u002Fimmunophenotype.py",19795,"https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002Fopenscience\u002Fbackend\u002Fcli\u002Fskills\u002Fbiology\u002Fflow-cytometry-analysis\u002Fscripts\u002Fimmunophenotype.py",[86,117,141,175,202,229],{"skillNo":87,"name":88,"nameZh":6,"description":89,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":90,"categories":91,"keywords":97,"publishedAt":39},"01M7167B28PK20HP7R7ZED2DY0","statistical-power","Sample size calculation, power analysis, and effect size conversion for scientific studies. Use BEFORE data collection (a priori) to determine required N, or after (post-hoc) to compute achieved power. Covers parametric tests (t-test, ANOVA, chi-square, proportions, correlation, regression), simulation-based power for complex\u002Fclustered\u002Fnested designs, and power curves. Use this skill when someone asks 'how many samples do I need', 'what is my power', 'what effect size can I detect', or any variant. Pairs with \u002Fexperimental-design (choose design first, then compute power) and \u002Fstatistical-analysis (analyze data after collection).","https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002Fscientific-agent-skills\u002Fskills\u002Fstatistical-power\u002FSKILL.md",[92],{"categoryNo":93,"categoryName":94,"taxonomyType":95,"taxonomyTypeDesc":96},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[98,99,100,101,102,103,104,105,106,107,108,21,109,110,111,112,113,114,115,116],"power","statistical power","effect","scipy","regression","choose","design","uv","when","Bash","collection","calculation","ANOVA","conversion","analyze","t-test","chi-square","simulation","sample",{"skillNo":118,"name":119,"nameZh":6,"description":120,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":121,"categories":122,"keywords":126,"publishedAt":39},"01M71677EBCM8EPCE9BZCK8505","statistical-analysis","Guided statistical analysis with test selection and reporting. Use when you need help choosing appropriate tests for your data, assumption checking, power analysis, and APA-formatted results. Best for academic research reporting, test selection guidance. For implementing specific models programmatically use statsmodels.","https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002Fscientific-agent-skills\u002Fskills\u002Fstatistical-analysis\u002FSKILL.md",[123],{"categoryNo":124,"categoryName":125,"taxonomyType":95,"taxonomyTypeDesc":96},"01JBSEEDCAT000000000000008","测试与质量",[21,127,98,128,129,130,106,131,132,133,25,134,34,27,135,136,137,138,139,140],"statistical analysis","academic research","implementing","selection","you","reporting","data","tests","test","research","results","statsmodels","models","help",{"skillNo":142,"name":143,"nameZh":6,"description":144,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":145,"categories":146,"keywords":156,"publishedAt":39},"01M71673R93XAHFH81DBNVNG7H","scientific-writing","Core skill for the deep research and writing tool. Write scientific manuscripts in full paragraphs (never bullet points). Use two-stage process with (1) section outlines with key points using research-lookup then (2) convert to flowing prose. IMRAD structure, citations (APA\u002FAMA\u002FVancouver), figures\u002Ftables, reporting guidelines (CONSORT\u002FSTROBE\u002FPRISMA), for research papers and journal submissions.","https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002Fscientific-agent-skills\u002Fskills\u002Fscientific-writing\u002FSKILL.md",[147,150,151],{"categoryNo":148,"categoryName":149,"taxonomyType":95,"taxonomyTypeDesc":96},"01JBSEEDCAT000000000000018","写作与文案",{"categoryNo":93,"categoryName":94,"taxonomyType":95,"taxonomyTypeDesc":96},{"categoryNo":152,"categoryName":153,"taxonomyType":154,"taxonomyTypeDesc":155},"01JBSEEDCAT000000000000036","内容创作者","PROFESSION","适用职业",[157,158,159,160,161,162,163,164,165,166,107,167,168,169,170,171,172,173,132,174],"writing","scientific","scientific writing","Citations","deep","deep research","never","papers","journal","false","api","prisma","structure","openrouter","Outlines","core skill","read","features",{"skillNo":176,"name":177,"nameZh":6,"description":178,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":179,"categories":180,"keywords":185,"publishedAt":39},"01M7166ZPKXBF4EBPHVTJPQBB1","scientific-visualization","Meta-skill for publication-ready figures. Use when creating journal submission figures requiring multi-panel layouts, significance annotations, error bars, colorblind-safe palettes, and specific journal formatting (Nature, Science, Cell). Orchestrates matplotlib\u002Fseaborn\u002Fplotly with publication styles. For quick exploration use seaborn or plotly directly.","https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002Fscientific-agent-skills\u002Fskills\u002Fscientific-visualization\u002FSKILL.md",[181,184],{"categoryNo":182,"categoryName":183,"taxonomyType":95,"taxonomyTypeDesc":96},"01JBSEEDCAT000000000000013","分析与可视化",{"categoryNo":93,"categoryName":94,"taxonomyType":95,"taxonomyTypeDesc":96},[186,158,187,188,189,190,191,192,193,165,106,194,195,196,197,198,199,200,25,201],"visualization","scientific visualization","error","ready","quick","exploration","meta-skill","Meta","safe","plotly","annotations","formatting","Publication","figures","panel","submission",{"skillNo":203,"name":204,"nameZh":6,"description":205,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":206,"categories":207,"keywords":214,"publishedAt":39},"01M7166VXJWQSSC3RQZP60WBGR","research-code","Debug, profile, review, and restructure Python research and analysis code. Use when analysis scripts crash, produce wrong results, run too slowly, or need a correctness review. Covers: reading tracebacks and diagnosing root causes, hypothesis-driven debugging, reviewing code for common statistical\u002Fnumerical errors, profiling data pipelines, improving reproducibility (seeds, environments, determinism), and structuring analysis scripts for sharing. Distinct from \u002Fexploratory-data-analysis (which understands data) and \u002Fstatistical-analysis (which chooses and runs tests) — this skill focuses on the code itself.","https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002Fscientific-agent-skills\u002Fskills\u002Fresearch-code\u002FSKILL.md",[208,211],{"categoryNo":209,"categoryName":210,"taxonomyType":95,"taxonomyTypeDesc":96},"01JBSEEDCAT000000000000021","资料检索与研究",{"categoryNo":212,"categoryName":213,"taxonomyType":154,"taxonomyTypeDesc":155},"01JBSEEDCAT000000000000030","开发工程师",[215,216,136,217,218,219,220,105,221,106,107,21,222,223,224,225,226,227,173,228],"code","research code","which","memory","data-analysis","errors","debugging","common","review","data pipelines","run","scripts","line","write",{"skillNo":230,"name":231,"nameZh":6,"description":232,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":233,"licenseFileUrl":6,"sourceHtmlUrl":234,"categories":235,"keywords":242,"publishedAt":39},"01M7166CF5HSR7SVZYZFWTDX55","markdown-mermaid-writing","Comprehensive markdown and Mermaid diagram writing skill. Use when creating any scientific document, report, analysis, or visualization. Establishes text-based diagrams as the default documentation standard with full style guides (markdown + mermaid), 24 diagram type references, and 9 document templates.","Apache-2.0","https:\u002F\u002Fgithub.com\u002Ffadrienne\u002FMyGitHub\u002Fblob\u002Fmain\u002Fscientific-agent-skills\u002Fskills\u002Fmarkdown-mermaid-writing\u002FSKILL.md",[236,239,240,241],{"categoryNo":237,"categoryName":238,"taxonomyType":95,"taxonomyTypeDesc":96},"01JBSEEDCAT000000000000002","文档处理",{"categoryNo":148,"categoryName":149,"taxonomyType":95,"taxonomyTypeDesc":96},{"categoryNo":93,"categoryName":94,"taxonomyType":95,"taxonomyTypeDesc":96},{"categoryNo":152,"categoryName":153,"taxonomyType":154,"taxonomyTypeDesc":155},[157,243,244,245,246,247,186,248,249,106,107,21,250,251,252,253,254,255,158,173],"markdown mermaid writing","mermaid","markdown","community","diagrams","project","default","team","guides","text","documentation","references","diagram"]