Skip to main content

Running the analysis

With your data in data/ and the parameters configured, you can run the notebook either cell-by-cell or all at once. When it finishes, head to the Interpretation Guide for a walkthrough of every output.

Pre-flight: check your data first

A run can take 10–30 minutes, so it’s worth a 5-second sanity check before you start. Paste the snippet below into a new cell right after your data loads — i.e. after the data DataFrame is created and your configuration cells have run.

Flexible by design This check is warn-only and locale-agnostic. It doesn’t assume any fixed column names — it simply compares the variable names you configured (TREATMENT_VAR, OUTCOME_VAR, PERSON_ID_VAR, SUBGROUP_VARS, and your confounder lists) against the columns actually in your export. If a name doesn’t match — common with non-English locales or custom HR attributes — it suggests the closest column instead of failing deep in the run. It never changes your data.

# --- Pre-flight data check (flexible, warn-only) --------------------------
# Run AFTER your data is loaded (`data` exists) and the config cells have run.
import difflib
import pandas as pd

try:
    data
except NameError:
    raise NameError("Load your data into `data` first (run the data-loading cell), then re-run this check.")

cols = list(data.columns)

def _resolve(name, role, required=False):
    if not name:
        return None
    if name in cols:
        return name
    near = difflib.get_close_matches(name, cols, n=3, cutoff=0.6)
    flag = "[X]" if required else "[!]"
    print(f"{flag} {role} '{name}' not found in your data.")
    if near:
        print(f"     closest columns: {near}")
    print("     -> update this name in your config, or remove it.")
    return None

# Names you configured (fall back gracefully if a list isn't defined)
treatment = globals().get('TREATMENT_VAR')
outcome   = globals().get('OUTCOME_VAR')
person    = globals().get('PERSON_ID_VAR', 'PersonId')
controls  = []
for listname in ('SUBGROUP_VARS', 'NETWORK_VARS', 'COLLABORATION_VARS'):
    controls += list(globals().get(listname, []) or [])
controls = list(dict.fromkeys(controls))

print(f"Rows: {len(data):,} | Columns: {len(cols)}")
if person in cols:
    print(f"Unique persons ({person}): {data[person].nunique():,}")
date_col = next((c for c in ('MetricDate', 'Date') if c in cols), None)
if date_col:
    print(f"Distinct dates ({date_col}): {data[date_col].nunique():,}")

# Essentials the run truly needs
t = _resolve(treatment, "Treatment", required=True)
y = _resolve(outcome,   "Outcome",   required=True)
p = person if person in cols else _resolve(person, "Person ID", required=True)

# Treatment variation & spread
if t:
    s = pd.to_numeric(data[t], errors='coerce')
    print(f"[ok] Treatment '{t}': min {s.min():g}, median {s.median():g}, max {s.max():g}")
    top_share = s.value_counts(normalize=True, dropna=True).max() if s.notna().any() else 1.0
    if s.nunique(dropna=True) <= 1:
        print("     [!] no variation in treatment - DML cannot estimate an effect.")
    elif top_share > 0.95:
        print(f"     [!] {top_share:.0%} of rows share one value - limited spread; "
              "check you have both low and high Copilot users.")

# Outcome sanity
if y:
    s = pd.to_numeric(data[y], errors='coerce')
    print(f"[ok] Outcome '{y}': {s.isna().mean()*100:.1f}% missing/non-numeric")

# Confounders / subgroup vars you configured
for name in controls:
    _resolve(name, "Configured variable")

# Missing-value summary for configured columns that exist
present = [c for c in [treatment, outcome, person, *controls] if c and c in cols]
miss = (data[present].isna().mean() * 100).round(1)
miss = miss[miss > 0]
if len(miss):
    print("[!] Missing values (these rows are dropped later):")
    print(miss.to_string())

# Available columns, to help you correct any name
print(f"\nAvailable columns ({len(cols)}): {', '.join(cols)}")
print("\n[i] Advisory only. Fix any [X] before running; [!] are safe to review and ignore.")

How to read it: [X] marks an essential variable (treatment, outcome, person ID) that the run genuinely can’t proceed without — fix these first. [!] is a heads-up (a misnamed confounder, missing values, or thin treatment spread) that’s safe to review and continue past. When a name isn’t found, the closest columns line usually points straight at the fix — for example, a French-locale export might show 'Collaboration_hours' not found → closest: ['Heures_de_collaboration'], so you update that one name in your config cell.

First run Run cell-by-cell the first time. It lets you catch errors early (missing columns, wrong paths), review intermediate outputs, understand each step, and adjust parameters before continuing.

Running all cells at once

Once you’re confident the notebook is configured correctly:

Expected runtime: 10–30 minutes depending on data size.

Troubleshooting common errors

FileNotFoundError: [Errno 2] No such file or directory

Cause: the data file path is incorrect or the file doesn’t exist.

  1. Check the CSV is in the copilot-causal-toolkit/data/ folder.
  2. Verify the filename in data_file_path matches exactly, including capitalization.
  3. Try a full absolute path: data_file_path = r"C:\Users\YourName\...\data\file.csv".
KeyError: 'ColumnName' or "Column not found"

Cause: a variable in the configuration doesn’t exist in your data.

  1. Check the list of columns your notebook prints early on.
  2. Update SUBGROUP_VARS, NETWORK_VARS, COLLABORATION_VARS to match your actual column names.
  3. Remove any variables from the lists that don’t exist in your dataset.
ValueError: could not convert string to float

Cause: a data-type mismatch or missing values in numeric columns.

Check for non-numeric values in metrics like Total_Copilot_actions_taken or the outcome variable.

MemoryError or the notebook becomes unresponsive

Cause: the dataset is too large for available memory.

  1. Filter to a smaller time period.
  2. Reduce the number of subgroups analyzed.
  3. Close other applications to free up memory.

Use GitHub Copilot for help If you have GitHub Copilot / Copilot Chat in VS Code: select the error message or problematic code, open Copilot Chat (Ctrl+Shift+I / Cmd+Shift+I), and ask specific questions — e.g. “Why am I getting this error?”, “How do I fix this KeyError for the column ‘Organization’?”, or “How do I change this variable list to use different column names?”

Still stuck?

FAQ

How much data do I need?

Aim for 6 months of weekly data (roughly 12–26 weeks). More person-weeks generally produce narrower confidence intervals and more reliable subgroup estimates.

I get "no significant subgroups" — what now?

This can mean there genuinely is no detectable effect, or that you have insufficient data / overlap to detect one. Check that both Copilot users and comparable non/low-users exist across covariate values, widen the time window, or reduce the number of subgroup attributes. See What this analysis can and cannot prove.

Which notebook do I use for my question?

Pick by outcome, not scenario label: external collaboration → CI-DML_ExtCollabHours_*, after-hours → CI-DML_AftCollabHours_*, survey engagement → CI-DML_Engagement_PQ. The overview has a card per scenario.

Can I use this with a Super Users Report instead of a Person Query?

Yes — use the _SUR.ipynb notebooks, which handle the report’s Date column. Note that some Person Query metrics aren’t available in the report; if critical confounders are missing, prefer a Person Query. See Preparing your data.

Next Run complete? Continue to the Interpretation Guide to read every output the toolkit produces.

Last updated: Jun 23, 2026 Edit this page on GitHub