Reproducing Pakhomov et al. (2023) from dandiset 000875¶

Paper. Pakhomov AG, Gudvangen E, Mangalanathan U, Kondratiev O, Redondo L, Semenov I (2023). Next generation CANCAN focusing for remote stimulation by nanosecond electric pulses. Bioelectrochemistry 152, 108437. doi:10.1016/j.bioelechem.2023.108437. The claims file was written from the accepted author manuscript in PubMed Central (PMC10247516), with Methods, Results, Discussion and legends; there is no supplement and no linked code.

Dandiset. DANDI:000875, version 0.240515.1925. The paper cites no dandiset or version. This is the only published version, and the draft lists the same nine assets (same asset ids and sizes), so the most recent published version is pinned.

What the dandiset holds. Nine stitched YO-PRO-1 (YP) images of CHO monolayers, one per dish, three dishes each after Packet 2 was rotated 1, 5 or 10 times over the four electrodes (1Px4, 5Px4, 10Px4; 4, 20 or 40 packets), first pulse 3.2 kV. This is the experiment of Fig. 6. The single-cell time-lapse of Fig. 4 and the monolayer experiments of Figs. 5, 7, 8 and 9 are not in it, and the Sim4Life model of Fig. 3 is not public.

Method. Each image is streamed from the archive. The four electrode footprints are found in an 8 x 8 block-mean overview, and mean intensities are measured in square ROIs along the diagonals of the full-resolution image, over +/- 1.65 mm from the array center (Methods 2.4, small array), under every reading the claims file names. The background reading b2 subtracts a dark offset estimated from the darkest parts of each image, since the electrode footprints turned out to hold cells. The pixel size is not in the files; 0.65 um per pixel (camera pixel through the 10x objective) is assumed, and checked against the electrode geometry. Helper: cancan.py.

Claim Status
fig6a-more-packets-more-uptake tested
fig6b-peak-relative-to-periphery tested
fig6-trough-half-of-center tested
fig6-peak-narrower tested
fig4-bipolar-cancellation, fig4-twenty-fold-cancellation blocked: data missing (single-cell time-lapse not in the dandiset)
fig5a-packet-without-rotation, fig5bc-rotation-focuses-center blocked: data missing (Fig. 5 dishes not in the dandiset)
fig7ab-amplitude-scales-uptake, fig7-targeting-independent-of-amplitude, fig7-targeting-independent-of-array-size blocked: data missing (only 3.2 kV, small array, 1Px4 to 10Px4)
fig8-packet2-best-targeting, fig8b-normalized-center, fig8-cancellation-predicts-targeting blocked: data missing (Packets 1 and 3, 4 kV, not in the dandiset; the last also needs Fig. 4)
fig9-gap-fades-periphery, fig9b-six-fold-targeting blocked: data missing (no lifted-array dishes)
fig3-field-simulation blocked: data missing (Sim4Life model not public)
In [1]:
import os
os.environ.setdefault("OMP_NUM_THREADS", "1")
import time
import warnings

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy import ndimage, stats

import cancan

pd.set_option("display.width", 220)
pd.set_option("display.max_columns", 40)
pd.set_option("display.max_rows", 400)
warnings.filterwarnings("ignore", category=RuntimeWarning)
TREATMENTS = ["1Px4", "5Px4", "10Px4"]
HIST_BINS = np.geomspace(20, 5000, 120)  # log-spaced bins for the intensity histograms, a plotting choice

started = time.time()
assets = cancan.list_assets()
dishes = {t: [] for t in TREATMENTS}
for a in assets:
    meta = cancan.read_metadata(a["asset_id"])
    overview = cancan.load_overview(a["asset_id"])
    electrodes = cancan.find_electrodes(overview)
    geo = cancan.geometry(electrodes)
    region, origin = cancan.load_region(a["asset_id"], geo["center"], 2700)
    profiles = cancan.dish_profiles(region, origin, electrodes)
    half_geo = cancan.geometric_half_span(electrodes)
    profiles_geo = cancan.dish_profiles(region, origin, electrodes, half_geo)
    k = 32  # an 80 x 80 thumbnail (blocks of 256 x 256 pixels), kept for the duplicate check and the figure
    h = overview.shape[0] // k * k
    thumb = overview[:h, :h].reshape(h // k, k, h // k, k).mean(axis=(1, 3))
    zones = cancan.zone_values(overview, electrodes)
    dark = cancan.dark_level(overview)
    # Blocks whose 3 x 3 neighbourhood is all nonzero cannot straddle the zero padding; the dark level from them alone is a check.
    interior = ndimage.binary_erosion(overview > 0, np.ones((3, 3)), border_value=0)
    dark_interior = float(np.percentile(overview[interior], 1.0))
    dark_touch = float((~interior[(overview > 0) & (overview <= dark)]).mean())
    R = np.mean([np.hypot(e["row"] - geo["center"][0], e["col"] - geo["center"][1]) for e in electrodes])
    dy, dx = np.nonzero((overview > 0) & (overview <= dark))
    dark_far = float((np.hypot(dy * cancan.BLOCK - geo["center"][0], dx * cancan.BLOCK - geo["center"][1]) > 2 * R).mean())
    # The array area of the overview (8 x 8 blocks) and a full-resolution crop of footprint 0 and its rim, for the figures.
    cb = (geo["center"] / cancan.BLOCK).astype(int)
    hw = int(1.45 * np.max([np.hypot(e["row"] - geo["center"][0], e["col"] - geo["center"][1]) for e in electrodes]) / cancan.BLOCK)
    array_crop = overview[max(0, cb[0] - hw):cb[0] + hw, max(0, cb[1] - hw):cb[1] + hw].copy()
    e0 = electrodes[0]
    fp_crop, fp_origin = cancan.load_region(a["asset_id"], (e0["row"], e0["col"]), int(1.6 * e0["radius"]))
    # Full-resolution 600 x 600 patches: the footprint center, and the monolayer 1.25 radii out, away from the array.
    u = np.array([e0["row"], e0["col"]]) - geo["center"]
    u /= np.linalg.norm(u)
    patches = {}
    for name, p in {"footprint": (e0["row"], e0["col"]), "outside": np.array([e0["row"], e0["col"]]) + 1.25 * e0["radius"] * u}.items():
        py, px = int(p[0]) - fp_origin[0], int(p[1]) - fp_origin[1]
        patches[name] = fp_crop[py - 300:py + 300, px - 300:px + 300].copy()
    s4 = fp_crop.shape[0] // 4 * 4, fp_crop.shape[1] // 4 * 4
    fp_small = fp_crop[:s4[0], :s4[1]].reshape(s4[0] // 4, 4, s4[1] // 4, 4).mean(axis=(1, 3))
    dishes[a["treatment"]].append({
        **a, "meta": meta, "electrodes": electrodes, "geometry": geo, "profiles": profiles,
        "profiles_geo": profiles_geo, "half_span_geo_mm": half_geo * cancan.PIXEL_UM / 1000,
        "footprint": cancan.footprint_level(overview, electrodes),
        "dark": dark, "dark_far_fraction": dark_far, "dark_interior": dark_interior, "dark_touch_zero": dark_touch, "region_min": float(region.min()),
        "dark_q": {q: cancan.dark_level(overview, q) for q in (0.1, 1.0, 5.0)},
        "zone_hist": {z: np.histogram(v, HIST_BINS)[0] for z, v in zones.items()},
        "zone_n": {z: len(v) for z, v in zones.items()},
        "zone_pct": {z: np.percentile(v, [1, 5, 50, 95, 99]) for z, v in zones.items()},
        "overview_stats": {"min": float(overview.min()), "max": float(overview.max()),
                           "median_nonzero": float(np.median(overview[overview > 0])),
                           "zero_fraction": float((overview == 0).mean())},
        "region_stats": {"min": int(region.min()), "max": int(region.max()), "median": float(np.median(region)),
                         "near_ceiling_fraction": float((region >= 65000).mean())},
        "thumb": thumb, "array_crop": array_crop, "array_crop_origin": (max(0, cb[0] - hw), max(0, cb[1] - hw)),
        "fp_small": fp_small, "fp_crop_origin": fp_origin, "patches": patches,
    })
    del overview, region, fp_crop
print(f"loaded {len(assets)} files from dandiset {cancan.DANDISET_ID} version {cancan.DANDISET_VERSION} "
      f"in {time.time() - started:.0f} s")
loaded 9 files from dandiset 000875 version 0.240515.1925 in 260 s

Data check¶

Each check of the metadata against the values is stated under its output.

In [2]:
rows = []
for t in TREATMENTS:
    for d in dishes[t]:
        m = d["meta"]
        desc = m["session_description"]
        cycles = int(desc.split("A total of ")[1].split(" cycles")[0])
        kv = float(desc.split("single ")[1].split(" kV")[0])
        rows.append({"treatment": t, "dish": d["dish"], "subject_id": m["subject_id"],
                     "cycles_in_description": cycles, "kV_in_description": kv, "kV_in_name": d["kv_from_name"],
                     "session_start": m["session_start_time"], "species": m["species"].split("/")[-1],
                     "strain": m["strain"], "age": m["age"], "shape": m["shape"], "unit": m["unit"],
                     "rate_Hz": m["rate"], "series": m["series_fields"], "size_MB": round(d["size"] / 1e6)})
info = pd.DataFrame(rows)
print("Files per treatment:", info.treatment.value_counts().reindex(TREATMENTS).to_dict())
print(info.drop(columns=["series"]).to_string(index=False))
print("\nPacket cycles in the description equal the packets per electrode in the name:",
      bool((info.cycles_in_description == info.treatment.str[:-3].astype(int)).all()))
print("First-pulse voltage in every description:", sorted(info.kV_in_description.unique()), "kV; in the names:",
      sorted(info.kV_in_name.unique()))
print("Fields of the ImageSeries:", info.series.iloc[0], "; groups in the file:", dishes["1Px4"][0]["meta"]["groups"])
shared = info.groupby("session_start").dish.apply(list)
print("\nSession start times shared by more than one dish:", shared[shared.map(len) > 1].to_dict())
print("Device description:", dishes["1Px4"][0]["meta"]["device_description"])
print("Experimenter:", dishes["1Px4"][0]["meta"]["experimenter"])
vm = cancan.version_metadata()
summ = vm["assetsSummary"]
print("\nArchive summary of the version: numberOfFiles", summ["numberOfFiles"], "; numberOfSubjects", summ["numberOfSubjects"],
      "; species", [x["name"] for x in summ.get("species", [])], "; distinct subject ids in the files", info.subject_id.nunique())
print("Dandiset description:", vm["description"])
Files per treatment: {'1Px4': 3, '5Px4': 3, '10Px4': 3}
treatment  dish                           subject_id  cycles_in_description  kV_in_description  kV_in_name             session_start        species        strain age             shape unit  rate_Hz  size_MB
     1Px4    23  1p_x4_600ns_200ms_3,2kV_YP_(dish23)                      1                3.2         3.2 2021-02-16T17:03:39-05:00 NCBITaxon_9913 cellline: CHO P1D (1, 20480, 20480) n.a.      2.0      378
     1Px4    24  1p_x4_600ns_200ms_3,2kV_YP_(dish24)                      1                3.2         3.2 2021-02-16T17:09:42-05:00 NCBITaxon_9913 cellline: CHO P1D (1, 20480, 20480) n.a.      2.0      385
     1Px4    25  1p_x4_600ns_200ms_3,2kV_YP_(dish25)                      1                3.2         3.2 2021-02-16T17:15:04-05:00 NCBITaxon_9913 cellline: CHO P1D (1, 20480, 20480) n.a.      2.0      371
     5Px4     9  5p_x4_600ns_200ms_3,2kV_YP_(dish09)                      5                3.2         3.2 2021-02-16T17:15:04-05:00 NCBITaxon_9913 cellline: CHO P1D (1, 20480, 20480) n.a.      2.0      407
     5Px4    20  5p_x4_600ns_200ms_3,2kV_YP_(dish20)                      5                3.2         3.2 2021-02-16T16:50:24-05:00 NCBITaxon_9913 cellline: CHO P1D (1, 20480, 20480) n.a.      2.0      413
     5Px4    22  5p_x4_600ns_200ms_3,2kV_YP_(dish22)                      5                3.2         3.2 2021-02-16T16:50:24-05:00 NCBITaxon_9913 cellline: CHO P1D (1, 20480, 20480) n.a.      2.0      394
    10Px4     4 10p_x4_600ns_200ms_3,2kV_YP_(dish04)                     10                3.2         3.2 2021-02-16T13:39:45-05:00 NCBITaxon_9913 cellline: CHO P1D (1, 20480, 20480) n.a.      2.0      419
    10Px4    11 10p_x4_600ns_200ms_3,2kV_YP_(dish11)                     10                3.2         3.2 2021-02-16T14:38:33-05:00 NCBITaxon_9913 cellline: CHO P1D (1, 20480, 20480) n.a.      2.0      421
    10Px4    18 10p_x4_600ns_200ms_3,2kV_YP_(dish18)                     10                3.2         3.2 2021-02-16T16:21:42-05:00 NCBITaxon_9913 cellline: CHO P1D (1, 20480, 20480) n.a.      2.0      421

Packet cycles in the description equal the packets per electrode in the name: True
First-pulse voltage in every description: [np.float64(3.2)] kV; in the names: [np.float64(3.2)]
Fields of the ImageSeries: ['data', 'starting_time'] ; groups in the file: ['acquisition', 'analysis', 'file_create_date', 'general', 'identifier', 'processing', 'session_description', 'session_start_time', 'specifications', 'stimulus', 'timestamps_reference_time']

Session start times shared by more than one dish: {'2021-02-16T16:50:24-05:00': [20, 22], '2021-02-16T17:15:04-05:00': [25, 9]}
Device description: IX83 microscope used; MS-2000 scanning stage; X-Cite 110LED illuminator; ORCA-Flash4 sCMOS camera;CellSens software for automation; ex/em 480/535nm for detecting electroporation using 1uM YOPRO-1; dark circles indicate electrode footprints; 144 images captured in a 12x12 grid from a six-well plate; all images automatically stitched for a final composite; 10×, 0.38NA objective used for high-resolution imaging.
Experimenter: ['Iurii Semenov']
Archive summary of the version: numberOfFiles 9 ; numberOfSubjects 18 ; species ['Bos taurus - Cattle'] ; distinct subject ids in the files 9
Dandiset description: This data provides images of Yo-Pro uptake when different numbers of nanosecond electric pulse packets were applied in a four-electrode array. Increasing the total number of applied packets from 4 to 20 and 40 significantly enhanced the electroporative effect across the electrode array. The peak of electroporation at the center was clearly distinguishable for all tested treatments. The study was supported in part by NIH R21EY034258

The nine files are three dishes each of 1Px4, 5Px4 and 10Px4 (Packet 2 applied 1, 5 or 10 times from each electrode, 4, 20 or 40 packets in all), first pulse 3.2 kV, as the session descriptions say and the file names agree. This is the experiment of Fig. 6 (Results 3.3, paragraph 1); the legend gives n = 3 to 4, and the dandiset has 3 per treatment. Each file holds one stitched image and nothing else: no ROI table, no pixel size, no electrode positions. The species is NCBITaxon 9913 (Bos taurus, cattle), while the strain field and the paper say CHO cells (Chinese hamster); the age "P1D" has no meaning for a cell line. The ImageSeries has unit "n.a." and a rate of 2 Hz for a single frame. Two pairs of dishes share a session start time to the second, one pair across treatments (dish 09 of 5Px4 and dish 25 of 1Px4); the images themselves differ (checked below), so the start times are not acquisition times of each dish. The "200ms" in every file name is not explained; packets were given at 1 Hz.

In [3]:
rows = []
for t in TREATMENTS:
    for d in dishes[t]:
        rows.append({"treatment": t, "dish": d["dish"], **{f"overview_{k}": round(v, 3) for k, v in d["overview_stats"].items()},
                     **{f"region_{k}": round(v, 5) if isinstance(v, float) else v for k, v in d["region_stats"].items()},
                     "footprint_median": round(d["footprint"], 1),
                     **{f"dark_p{q:g}": round(v, 1) for q, v in d["dark_q"].items()},
                     "roi_saturated_max": max(float(p["saturated"].max()) for p in d["profiles"].values())})
vals = pd.DataFrame(rows)
print("Intensities are uint16 camera counts (unit 'n.a.', conversion 1, offset 0).")
print("overview_*: 8 x 8 block means of the whole 20480 x 20480 image; region_*: the full-resolution square around the array center;")
print("dark_p*: percentiles of the nonzero block means of the whole image.")
print(vals.to_string(index=False))
Intensities are uint16 camera counts (unit 'n.a.', conversion 1, offset 0).
overview_*: 8 x 8 block means of the whole 20480 x 20480 image; region_*: the full-resolution square around the array center;
dark_p*: percentiles of the nonzero block means of the whole image.
treatment  dish  overview_min  overview_max  overview_median_nonzero  overview_zero_fraction  region_min  region_max  region_median  region_near_ceiling_fraction  footprint_median  dark_p0.1  dark_p1  dark_p5  roi_saturated_max
     1Px4    23           0.0     65434.859                  278.562                   0.259         110       65440          509.0                       0.00001             295.2       58.6     73.5    102.1           0.000000
     1Px4    24           0.0     64654.812                  278.344                   0.259         118       65442          482.0                       0.00001             265.9       59.0     86.7    153.1           0.000000
     1Px4    25           0.0     57509.406                  228.469                   0.259         110       65437          411.0                       0.00000             238.5       58.2     76.2     99.2           0.000000
     5Px4     9           0.0     65435.312                  338.547                   0.259         171       65442         1180.0                       0.00007             349.7       59.9     76.7    121.9           0.005131
     5Px4    20           0.0     65434.969                  297.812                   0.258         129       65445         1727.0                       0.00018             312.0       58.5     78.5    115.1           0.007225
     5Px4    22           0.0     59244.906                  258.719                   0.258         160       64855          973.0                       0.00000             292.8       58.5     73.8    107.9           0.000000
    10Px4     4           0.0     65435.109                  309.859                   0.258         186       65445         2330.0                       0.00085             332.8       60.7     87.1    122.9           0.024951
    10Px4    11           0.0     65435.281                  364.469                   0.258         200       65446         1964.0                       0.00072             402.8       59.5     81.0    107.9           0.049589
    10Px4    18           0.0     65391.750                  294.859                   0.258         173       65447         2798.0                       0.00034             365.5       58.5     81.3    104.0           0.009849

Pixels are 16-bit counts; the zero pixels (26% of each image) are the padding outside the stitched tiles. The brightest pixels reach about 65440, the ceiling of these images; the fraction of pixels at or above 65000 in the central region is printed as region_near_ceiling_fraction, and the largest such fraction in any ROI as roi_saturated_max: none in 1Px4, up to 5.0% of an ROI in 10Px4, so the brightest 10Px4 ROIs, near the electrodes, are slightly capped. This lowers the 10Px4 periphery, which works against the order tested in fig6a. The median inside the electrode footprints is 239 to 403 counts; the darkest pixel of the central region is 110 to 200 counts; the 1st percentile of each image's nonzero block means is 73.5 to 87.1 counts (0.1st percentile 58.2 to 60.7, 5th 99.2 to 153.1). These are the candidate background levels examined below.

In [4]:
rows = []
for t in TREATMENTS:
    for d in dishes[t]:
        g = d["geometry"]
        rows.append({"treatment": t, "dish": d["dish"], "center_row": round(g["center"][0]), "center_col": round(g["center"][1]),
                     "footprint_diam_mm": np.round(g["footprint_diameter_mm"], 2).tolist(),
                     "diag_center_to_center_mm": np.round(g["diag_cc_mm"], 2).tolist(),
                     "diag_between_footprints_mm": np.round(g["diag_inner_mm"], 2).tolist(),
                     "sides_mm": np.round(g["side_cc_mm"], 2).tolist()})
geo = pd.DataFrame(rows)
print(f"Distances at the assumed {cancan.PIXEL_UM} um per pixel:")
print(geo.to_string(index=False))
inner = np.concatenate([d["geometry"]["diag_inner_mm"] for t in TREATMENTS for d in dishes[t]])
diam = np.concatenate([d["geometry"]["footprint_diameter_mm"] for t in TREATMENTS for d in dishes[t]])
cc = np.concatenate([d["geometry"]["diag_cc_mm"] for t in TREATMENTS for d in dishes[t]])
print(f"\nFootprint diameter {diam.min():.2f} to {diam.max():.2f} mm (median {np.median(diam):.2f}); needle diameter 1.5 mm (Methods 2.3).")
print(f"Diagonal between footprint edges {inner.min():.2f} to {inner.max():.2f} mm.")
print(f"Small array: ROIs cover +/- 1.65 mm = 3.30 mm; the margin left at each end is {(inner.min() - 3.3) / 2:.2f} to {(inner.max() - 3.3) / 2:.2f} mm,"
      f" {100 * (inner.min() - 3.3) / 2 / inner.min():.1f}% to {100 * (inner.max() - 3.3) / 2 / inner.max():.1f}% of the diagonal (Methods 2.4: '<5% of the diagonal length' excluded).")
print(f"Large array: ROIs cover +/- 3.65 mm = 7.30 mm, more than the {inner.max():.2f} mm between footprints at this scale.")
hg = [d["half_span_geo_mm"] for t in TREATMENTS for d in dishes[t]]
print(f"Variant extent, 5% of the footprint-to-footprint diagonal short of each footprint: +/- {min(hg):.2f} to {max(hg):.2f} mm at this scale (main: +/- 1.65 mm).")

# Scale check against the array sizes of the paper (Methods 2.3, 2.4; Section 3.5).
print("\nScale check. The paper: large array 8.4 mm diagonal (Section 3.5); 'approximately a 2-fold size difference' (Methods 2.3);")
print("needles 1.5 mm (Methods 2.3); ROIs over +/- 1.65 mm (small) and +/- 3.65 mm (large), '<5% of the diagonal length' excluded (Methods 2.4).")
print(f"  If 8.4 mm were center to center, the large array would leave {8.4 - 1.5:.2f} mm between needle edges, less than its own 7.30 mm of ROIs;"
      " so the paper's ROI extent fits only if the diagonal is read between needle edges.")
print(f"  Read between needle edges, the large array's ROIs leave {100 * (8.4 - 7.3) / 2 / 8.4:.1f}% of the diagonal at each end; the same"
      f" proportion gives a small-array diagonal of {3.3 / (7.3 / 8.4):.2f} mm, and half of 8.4 mm is 4.20 mm.")
for scale in [cancan.PIXEL_UM, 0.53]:
    f = scale / cancan.PIXEL_UM
    edge = cc * f - 1.5
    print(f"  At {scale} um per pixel: center-to-center diagonal {cc.min() * f:.2f} to {cc.max() * f:.2f} mm; between 1.5-mm needle edges"
          f" {edge.min():.2f} to {edge.max():.2f} mm; between dark footprint edges {inner.min() * f:.2f} to {inner.max() * f:.2f} mm;"
          f" the 3.30 mm of small-array ROIs fit between the footprints: {bool(inner.min() * f > 3.3)}.")
scale_needle = 1.5 / np.median(diam) * cancan.PIXEL_UM
print(f"  Scale that makes the median footprint exactly 1.5 mm: {scale_needle:.3f} um per pixel; scale that makes the center-to-center diagonal"
      f" 4.2 mm (half of 8.4 mm read center to center): {4.2 / np.median(cc) * cancan.PIXEL_UM:.3f} um per pixel.")
Distances at the assumed 0.65 um per pixel:
treatment  dish  center_row  center_col        footprint_diam_mm diag_center_to_center_mm diag_between_footprints_mm                 sides_mm
     1Px4    23        9701        9559 [1.72, 1.84, 1.83, 1.83]             [5.24, 5.32]               [3.47, 3.48]  [3.58, 3.7, 3.92, 3.74]
     1Px4    24       10368        8506 [1.81, 1.82, 1.68, 1.79]             [5.35, 5.31]                 [3.6, 3.5] [3.72, 3.69, 3.89, 3.78]
     1Px4    25        9911        8456 [1.95, 1.87, 1.66, 1.75]              [5.4, 5.35]                [3.6, 3.55] [3.78, 3.73, 3.88, 3.82]
     5Px4     9        8392        7519   [1.88, 1.8, 1.7, 1.77]             [5.28, 5.23]               [3.49, 3.45] [3.66, 3.72, 3.84, 3.65]
     5Px4    20       10929        9161  [2.06, 1.93, 1.91, 1.9]              [5.4, 5.35]               [3.42, 3.43]  [3.6, 3.87, 3.98, 3.77]
     5Px4    22       10426       10119  [1.92, 1.88, 1.8, 1.88]             [5.37, 5.42]               [3.52, 3.54]  [3.7, 3.86, 3.86, 3.85]
    10Px4     4       10303        8172  [2.09, 1.87, 1.79, 1.8]             [5.34, 5.44]                [3.4, 3.61]  [3.68, 3.8, 3.99, 3.79]
    10Px4    11       10906        9009 [1.96, 1.85, 1.77, 1.81]             [5.29, 5.38]               [3.43, 3.55]  [3.56, 3.8, 3.98, 3.77]
    10Px4    18       11462        9565  [1.96, 1.76, 1.7, 1.91]             [5.43, 5.55]               [3.59, 3.71] [3.71, 3.92, 3.92, 3.98]

Footprint diameter 1.66 to 2.09 mm (median 1.83); needle diameter 1.5 mm (Methods 2.3).
Diagonal between footprint edges 3.40 to 3.71 mm.
Small array: ROIs cover +/- 1.65 mm = 3.30 mm; the margin left at each end is 0.05 to 0.21 mm, 1.5% to 5.6% of the diagonal (Methods 2.4: '<5% of the diagonal length' excluded).
Large array: ROIs cover +/- 3.65 mm = 7.30 mm, more than the 3.71 mm between footprints at this scale.
Variant extent, 5% of the footprint-to-footprint diagonal short of each footprint: +/- 1.54 to 1.64 mm at this scale (main: +/- 1.65 mm).

Scale check. The paper: large array 8.4 mm diagonal (Section 3.5); 'approximately a 2-fold size difference' (Methods 2.3);
needles 1.5 mm (Methods 2.3); ROIs over +/- 1.65 mm (small) and +/- 3.65 mm (large), '<5% of the diagonal length' excluded (Methods 2.4).
  If 8.4 mm were center to center, the large array would leave 6.90 mm between needle edges, less than its own 7.30 mm of ROIs; so the paper's ROI extent fits only if the diagonal is read between needle edges.
  Read between needle edges, the large array's ROIs leave 6.5% of the diagonal at each end; the same proportion gives a small-array diagonal of 3.80 mm, and half of 8.4 mm is 4.20 mm.
  At 0.65 um per pixel: center-to-center diagonal 5.23 to 5.55 mm; between 1.5-mm needle edges 3.73 to 4.05 mm; between dark footprint edges 3.40 to 3.71 mm; the 3.30 mm of small-array ROIs fit between the footprints: True.
  At 0.53 um per pixel: center-to-center diagonal 4.27 to 4.53 mm; between 1.5-mm needle edges 2.77 to 3.03 mm; between dark footprint edges 2.78 to 3.03 mm; the 3.30 mm of small-array ROIs fit between the footprints: False.
  Scale that makes the median footprint exactly 1.5 mm: 0.532 um per pixel; scale that makes the center-to-center diagonal 4.2 mm (half of 8.4 mm read center to center): 0.510 um per pixel.

The files give no pixel size. At 0.65 um per pixel (the 6.5 um pixel of the ORCA-Flash4 camera through the 10x objective, both named in the file's device description and Methods 2.4), the four footprints sit on a near square with 5.23 to 5.55 mm center-to-center diagonals and 3.40 to 3.71 mm between the dark footprint edges. The small array's +/- 1.65 mm of ROIs then fits between the footprints with 1.5 to 5.6% of the diagonal left at each end, so in the narrowest dishes the outer ROIs reach into the 5% the Methods exclude (the variant that sets the ROI extent from the footprints avoids this), and the large array's +/- 3.65 mm does not fit at all, so the images are of the small array.

Two cues point to a smaller scale. The dark footprints are 1.66 to 2.09 mm across, against 1.5-mm needles (0.532 um per pixel would make the median footprint 1.5 mm), and half of the large array's 8.4 mm diagonal, read center to center, is 4.2 mm (0.510 um per pixel). The scale check above weighs them against the paper's own ROI extent. Read center to center, 8.4 mm would leave 6.90 mm between the large array's needles, less than the 7.30 mm its ROIs cover, so the paper's diagonal is between needle edges; on that reading the large array's ROIs leave 6.5% of it at each end, and the same proportion gives a small-array diagonal of 3.80 mm (half of 8.4 mm is 4.20 mm). At 0.65 um per pixel the small array measures 3.73 to 4.05 mm between 1.5-mm needle edges, which agrees; at 0.53 um it measures 2.77 to 3.03 mm, too short for the paper's 3.30 mm of ROIs. So the camera scale is kept, as an assumption, and the dark footprints are taken to be wider than the needles. The variant that sets the ROI extent from the footprints uses no pixel size, and each claim gives its outcome under it.

In [5]:
names = [(t, d["dish"]) for t in TREATMENTS for d in dishes[t]]
thumbs = [np.log(np.clip(d["thumb"], 1, None)) for t in TREATMENTS for d in dishes[t]]
corr = np.array([[np.corrcoef(a.ravel(), b.ravel())[0, 1] for b in thumbs] for a in thumbs])
identical = [(names[i], names[j]) for i in range(len(thumbs)) for j in range(i + 1, len(thumbs))
             if np.array_equal(thumbs[i], thumbs[j])]
print("Pairs of identical images:", identical if identical else "none")
off = corr[~np.eye(len(corr), dtype=bool)]
print(f"Correlation of log thumbnails between different dishes: {off.min():.3f} to {off.max():.3f}")
centers = np.array([d["geometry"]["center"] for t in TREATMENTS for d in dishes[t]])
print("Array centers (row, col) differ between dishes:", len({tuple(np.round(c, -1)) for c in centers}) == len(centers))
pair_corr = {f"{names[i][0]} dish{names[i][1]} / {names[j][0]} dish{names[j][1]}": round(corr[i, j], 3)
             for i in range(len(names)) for j in range(i + 1, len(names))
             if {names[i][1], names[j][1]} in ({20, 22}, {9, 25})}
print("Dishes that share a session start time, correlation of thumbnails:", pair_corr)

fig, axes = plt.subplots(3, 3, figsize=(12, 12))
for ax, (t, d) in zip(axes.flat, [(t, d) for t in TREATMENTS for d in dishes[t]]):
    th = d["thumb"]
    ax.imshow(np.clip(th, 0, np.percentile(th[th > 0], 99)), cmap="gray")
    c = np.array([[e["row"], e["col"]] for e in d["electrodes"]]) / 256
    for e in d["electrodes"]:
        ax.add_patch(plt.Circle((e["col"] / 256, e["row"] / 256), e["radius"] / 256, fill=False, color="y"))
    cen = c.mean(0)
    half = cancan.HALF_SPAN_MM * 1000 / cancan.PIXEL_UM / 256
    for a_, b_ in [(0, 2), (1, 3)]:
        u = (c[b_] - c[a_]) / np.linalg.norm(c[b_] - c[a_])
        ax.plot([cen[1] - half * u[1], cen[1] + half * u[1]], [cen[0] - half * u[0], cen[0] + half * u[0]], "c-", lw=1)
    ax.set_title(f"{t} dish {d['dish']}", fontsize=9)
    ax.set_xticks([]); ax.set_yticks([])
plt.suptitle("Thumbnails (blocks of 256 x 256 pixels), detected footprints (yellow) and the ROI lines (cyan)")
plt.tight_layout(); plt.show()
print("Display: each thumbnail clipped at its 99th percentile of nonzero blocks.")
Pairs of identical images: none
Correlation of log thumbnails between different dishes: 0.943 to 0.997
Array centers (row, col) differ between dishes: True
Dishes that share a session start time, correlation of thumbnails: {'1Px4 dish25 / 5Px4 dish9': np.float64(0.983), '5Px4 dish20 / 5Px4 dish22': np.float64(0.981)}
No description has been provided for this image
Display: each thumbnail clipped at its 99th percentile of nonzero blocks.

No two images are identical, and the array sits at a different place in each, so no recording was deposited twice; the shared start times are a metadata problem only. The footprints and ROI lines drawn on the thumbnails show the detection. Which electrode is e1 cannot be told from the images, but every treatment here rotates the packet over all four electrodes, so the diagonals are equivalent; diagonal A joins footprints 0 and 2 in the order of their angle around the center.

There is no time axis or trial table to align: each file is one image taken after the treatment. The check that the treatment labels belong to the images is the expected contrast between treatments, which is the subject of the first claim below.

What the electrode footprints hold, and the dark offset¶

Reading b2 of the claims file subtracts "a cell-free or dark offset, where the data hold one". The first round used the median inside the dark electrode footprints. The cells below show what the footprints hold at full resolution, the intensity distributions of the footprints, of the monolayer outside the array and of the exposed area between the electrodes, and where the darkest blocks of each image lie.

In [6]:
from scipy.ndimage import gaussian_filter

alld = [(t, d) for t in TREATMENTS for d in dishes[t]]
fig, axes = plt.subplots(3, 3, figsize=(13, 13))
for ax, (t, d) in zip(axes.flat, alld):
    im = d["array_crop"]
    o = d["array_crop_origin"]
    ax.imshow(np.log(np.clip(im, 1, None)), cmap="gray", vmin=np.log(np.percentile(im[im > 0], 1)), vmax=np.log(np.percentile(im[im > 0], 99.5)))
    c = np.array([[e["row"], e["col"]] for e in d["electrodes"]]) / cancan.BLOCK - np.array(o)
    for e in d["electrodes"]:
        ax.add_patch(plt.Circle((e["col"] / cancan.BLOCK - o[1], e["row"] / cancan.BLOCK - o[0]), e["radius"] / cancan.BLOCK, fill=False, color="y", lw=0.8))
    cen = c.mean(0)
    half = cancan.HALF_SPAN_MM * 1000 / cancan.PIXEL_UM / cancan.BLOCK
    for a_, b_ in [(0, 2), (1, 3)]:
        u = (c[b_] - c[a_]) / np.linalg.norm(c[b_] - c[a_])
        ax.plot([cen[1] - half * u[1], cen[1] + half * u[1]], [cen[0] - half * u[0], cen[0] + half * u[0]], "c-", lw=0.8)
    ax.set_title(f"{t} dish {d['dish']}", fontsize=9); ax.set_xticks([]); ax.set_yticks([])
plt.suptitle("Array area at 8 x 8 block resolution (5.2 um per block at the assumed scale), log scale, clipped at the 1st and 99.5th percentiles:\n"
             "fitted footprint circles (yellow) and the ROI lines over +/- 1.65 mm (cyan)")
plt.tight_layout(); plt.show()

fig, axes = plt.subplots(3, 3, figsize=(13, 13))
for ax, (t, d) in zip(axes.flat, alld):
    im = d["fp_small"]
    o = d["fp_crop_origin"]
    e = d["electrodes"][0]
    ax.imshow(im, cmap="gray", vmin=np.percentile(im, 1), vmax=np.percentile(im, 99.5))
    ax.add_patch(plt.Circle(((e["col"] - o[1]) / 4, (e["row"] - o[0]) / 4), e["radius"] / 4, fill=False, color="y", lw=0.8))
    ax.set_title(f"{t} dish {d['dish']}: footprint 0", fontsize=9); ax.set_xticks([]); ax.set_yticks([])
plt.suptitle("Footprint 0 of each dish and its rim (full resolution shown in 4 x 4 pixel means), linear scale clipped at the 1st and 99.5th percentiles")
plt.tight_layout(); plt.show()

fig, axes = plt.subplots(3, 6, figsize=(18, 9.5))
rows = []
for k, (t, d) in enumerate(alld):
    for j, name in enumerate(["footprint", "outside"]):
        z = d["patches"][name]
        ax = axes.flat[2 * k + j]
        ax.imshow(z, cmap="gray", vmin=np.percentile(z, 1), vmax=np.percentile(z, 95))
        ax.set_title(f"{t} d{d['dish']}: {name}", fontsize=8); ax.set_xticks([]); ax.set_yticks([])
    # Band-pass (difference of Gaussians, sigma 3 and 15 pixels, about 2 and 10 um): keeps structure of the size of nuclei, removes shot noise and gradients.
    tex = {name: gaussian_filter(d["patches"][name].astype(float), 3) - gaussian_filter(d["patches"][name].astype(float), 15) for name in ["footprint", "outside"]}
    rows.append({"treatment": t, "dish": d["dish"],
                 **{f"{name}_median": round(float(np.median(d["patches"][name])), 1) for name in tex},
                 **{f"{name}_texture_counts": round(float(np.subtract(*np.percentile(tex[name], [75, 25]))), 1) for name in tex}})
plt.suptitle("Full-resolution 600 x 600 pixel patches (0.39 mm at the assumed scale): the center of footprint 0, and the monolayer 1.25 radii\n"
             "from its center on the side away from the array; each clipped at its own 1st and 95th percentiles (the brightest debris saturates)")
plt.tight_layout(); plt.show()
print("Texture: interquartile range (counts) of the band-passed patch (difference of Gaussians, sigma 3 and 15 pixels), structure the size of nuclei.")
print(pd.DataFrame(rows).to_string(index=False))
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
Texture: interquartile range (counts) of the band-passed patch (difference of Gaussians, sigma 3 and 15 pixels), structure the size of nuclei.
treatment  dish  footprint_median  outside_median  footprint_texture_counts  outside_texture_counts
     1Px4    23             292.0           305.0                      11.2                    36.0
     1Px4    24             217.0           277.0                       9.7                    32.6
     1Px4    25             246.0           240.0                       8.6                    28.8
     5Px4     9             369.0           419.0                      16.5                    79.6
     5Px4    20             373.0           408.0                      21.4                   201.9
     5Px4    22             279.0           359.0                       7.6                    80.7
    10Px4     4             284.0           387.0                       9.6                   131.5
    10Px4    11             410.0           613.0                      16.6                   195.6
    10Px4    18             294.0           496.0                      14.6                   138.2
In [7]:
fig, axes = plt.subplots(3, 3, figsize=(14, 10), sharex=True)
centers = np.sqrt(HIST_BINS[:-1] * HIST_BINS[1:])
rows = []
for ax, (t, d) in zip(axes.flat, alld):
    for z, col in [("footprint", "k"), ("outside", "tab:green"), ("between", "tab:red")]:
        h = d["zone_hist"][z]
        ax.plot(centers, h / h.sum(), color=col, label=z)
    for v, ls, lab in [(d["dark"], "--", "b2-dark"), (d["region_min"], ":", "b2-min")]:
        ax.axvline(v, color="gray", ls=ls, label=lab)
    ax.set_xscale("log"); ax.set_title(f"{t} dish {d['dish']}", fontsize=9)
    rows.append({"treatment": t, "dish": d["dish"],
                 **{f"{z}_p1/p50/p99": " / ".join(f"{x:.0f}" for x in d["zone_pct"][z][[0, 2, 4]]) for z in ["footprint", "outside", "between"]},
                 "dark_p1": round(d["dark"], 1), "region_min": d["region_min"],
                 "darkest_1pct_beyond_2R": round(d["dark_far_fraction"], 3)})
axes.flat[0].legend(fontsize=8)
for ax in axes[-1]:
    ax.set_xlabel("block mean (counts)")
plt.suptitle("Distributions of 8 x 8 block means: inside the footprints (within 0.7 radius), the monolayer outside the array\n"
             "(1.2 to 1.6 times the center-to-footprint distance), and the exposed area between the electrodes (within half that distance)")
plt.tight_layout(); plt.show()
print(pd.DataFrame(rows).to_string(index=False))
for z in ["footprint", "outside", "between"]:
    frac = [d["zone_hist"][z].sum() / d["zone_n"][z] for _, d in alld]
    print(f"{z}: fraction of blocks within the plotted range {HIST_BINS[0]:.0f} to {HIST_BINS[-1]:.0f} counts, {min(frac):.3f} to {max(frac):.3f}")
fo = np.array([d["footprint"] / d["zone_pct"]["outside"][2] for _, d in alld])
print(f"\nFootprint median over the median of the monolayer outside the array: {fo.min():.2f} to {fo.max():.2f}")
print("Darkest 1% of nonzero blocks farther than 2 center-to-footprint distances from the array center:",
      f"{min(d['dark_far_fraction'] for _, d in alld):.3f} to {max(d['dark_far_fraction'] for _, d in alld):.3f}")
print("Darkest 1% of nonzero blocks that touch a zero block (and so may straddle the zero padding):",
      f"{min(d['dark_touch_zero'] for _, d in alld):.3f} to {max(d['dark_touch_zero'] for _, d in alld):.3f}")
print("1st percentile of the nonzero blocks away from the padding (3 x 3 neighbourhood all nonzero), per dish:",
      ", ".join(f"{d['dish']} {d['dark_interior']:.1f}" for _, d in alld),
      f"(b2-dark: {min(d['dark'] for _, d in alld):.1f} to {max(d['dark'] for _, d in alld):.1f})")
print(f"b2-dark offsets {min(d['dark'] for _, d in alld):.1f} to {max(d['dark'] for _, d in alld):.1f} counts; "
      f"b2-min offsets {min(d['region_min'] for _, d in alld):.0f} to {max(d['region_min'] for _, d in alld):.0f}; "
      f"footprint medians {min(d['footprint'] for _, d in alld):.0f} to {max(d['footprint'] for _, d in alld):.0f}.")
fd = np.array([d["footprint"] / d["dark"] for _, d in alld])
print(f"Footprint median over the b2-dark offset: {fd.min():.1f} to {fd.max():.1f}")
No description has been provided for this image
treatment  dish footprint_p1/p50/p99 outside_p1/p50/p99 between_p1/p50/p99  dark_p1  region_min  darkest_1pct_beyond_2R
     1Px4    23      236 / 295 / 443    241 / 318 / 583   303 / 540 / 1300     73.5       110.0                   1.000
     1Px4    24      200 / 266 / 384    220 / 294 / 578   292 / 496 / 1170     86.7       118.0                   1.000
     1Px4    25      195 / 239 / 372    195 / 254 / 517    252 / 423 / 992     76.2       110.0                   0.999
     5Px4     9      241 / 350 / 715   254 / 428 / 1404  466 / 1099 / 4265     76.7       171.0                   0.998
     5Px4    20     180 / 312 / 1410   168 / 376 / 1635 333 / 1563 / 10906     78.5       129.0                   0.997
     5Px4    22      231 / 293 / 546    214 / 312 / 843   438 / 964 / 3477     73.8       160.0                   1.000
    10Px4     4     255 / 333 / 1028   242 / 449 / 2344 672 / 1990 / 16980     87.1       186.0                   0.996
    10Px4    11     313 / 403 / 1863   301 / 544 / 2540 527 / 1624 / 10875     81.0       200.0                   1.000
    10Px4    18     263 / 365 / 1229   233 / 445 / 2513 720 / 2229 / 18702     81.3       173.0                   0.996
footprint: fraction of blocks within the plotted range 20 to 5000 counts, 0.998 to 1.000
outside: fraction of blocks within the plotted range 20 to 5000 counts, 0.997 to 1.000
between: fraction of blocks within the plotted range 20 to 5000 counts, 0.865 to 1.000

Footprint median over the median of the monolayer outside the array: 0.74 to 0.94
Darkest 1% of nonzero blocks farther than 2 center-to-footprint distances from the array center: 0.996 to 1.000
Darkest 1% of nonzero blocks that touch a zero block (and so may straddle the zero padding): 0.062 to 0.089
1st percentile of the nonzero blocks away from the padding (3 x 3 neighbourhood all nonzero), per dish: 23 74.2, 24 88.0, 25 76.9, 9 77.5, 20 79.7, 22 74.5, 4 88.5, 11 82.1, 18 82.5 (b2-dark: 73.5 to 87.1)
b2-dark offsets 73.5 to 87.1 counts; b2-min offsets 110 to 200; footprint medians 239 to 403.
Footprint median over the b2-dark offset: 3.1 to 5.0

The footprints are not cell-free. At full resolution several show a faint monolayer, with structure the size of nuclei (band-passed interquartile range 7.6 to 21.4 counts, against 28.8 to 201.9 in the monolayer just outside), and their median is 0.74 to 0.94 of the median of the monolayer outside the array and 3.1 to 5.0 times the darkest blocks. They read as cells that took up little dye, so the footprint median is not the "cell-free or dark offset" of reading b2. It is kept only as the variant b2-fp, which counts towards no outcome.

The darkest 1% of each image's nonzero blocks lie, for 99.6 to 100% of them, more than twice the center-to-footprint distance from the array center. The thumbnails suggest this is outside the circular edge of the dish's glass bottom, but nothing printed here shows it. 6.2 to 8.9% of these blocks touch a zero block and could straddle the zero padding, which would pull them down; the 1st percentile of the blocks away from the padding (3 x 3 neighbourhood all nonzero) is 74.2 to 88.5 counts, within 1.4 counts of the value with all blocks, so the padding does not set the offset. Their upper bound, the 1st percentile (73.5 to 87.1 counts), is taken as the dark offset b2-dark; the darkest pixel of the region around the array (110 to 200 counts) is a second reading, b2-min. The files hold no dark frame, so both are estimates, and fig6b shows how its result depends on the size of the offset.

In [8]:
def table(layout, background, extent="camera"):
    """Per-dish ROI values for a layout and background reading, by treatment.

    extent "camera": ROIs over +/- 1.65 mm at the assumed pixel size (main); "geometric": the
    variant that ends the ROIs 5% of the diagonal short of the footprints.
    """
    key = "profiles" if extent == "camera" else "profiles_geo"
    return {t: cancan.dish_matrix([{"profiles": d[key], "footprint": d["footprint"], "dark": d["dark"], "region_min": d["region_min"]}
                                   for d in dishes[t]], layout, background)
            for t in TREATMENTS}

pos, _ = table("r2", "b1")["1Px4"]
for lay in cancan.LAYOUTS:
    p, _ = table(lay, "b1")["1Px4"]
    print(f"{lay}: {len(p)} ROIs, {p[0]:.3f} to {p[-1]:.3f} mm, spacing {1000 * (p[1] - p[0]):.1f} um")
rows = []
for t in TREATMENTS:
    for lay in cancan.LAYOUTS:
        _, P = table(lay, "b1")[t]
        rows.append({"treatment": t, "layout": lay, "roi_min": P.min().round(1), "roi_max": P.max().round(1),
                     "dish_means": np.round(P.mean(axis=1), 1).tolist()})
print(pd.DataFrame(rows).to_string(index=False))
r2: 46 ROIs, -1.614 to 1.614 mm, spacing 71.7 um
r1-dA: 92 ROIs, -1.632 to 1.632 mm, spacing 35.9 um
r1-dB: 92 ROIs, -1.632 to 1.632 mm, spacing 35.9 um
treatment layout  roi_min  roi_max               dish_means
     1Px4     r2    363.8    946.5    [627.3, 567.0, 495.5]
     1Px4  r1-dA    281.3   2070.9    [619.5, 556.6, 514.6]
     1Px4  r1-dB    332.8   1778.1    [657.1, 587.6, 484.6]
     5Px4     r2    867.3   6689.6 [1653.5, 2673.3, 1309.8]
     5Px4  r1-dA    591.2   9880.5 [1466.2, 2504.9, 1216.2]
     5Px4  r1-dB    616.7  11820.3 [1765.2, 2816.0, 1390.5]
    10Px4     r2   1280.7  15942.7 [3954.4, 3000.5, 4615.1]
    10Px4  r1-dA    832.9  18520.7 [3073.4, 2889.1, 3756.7]
    10Px4  r1-dB    864.5  23280.2 [4736.9, 3229.9, 5164.5]

The readings of the claims file¶

The claims file leaves the ROI layout, background, center, periphery and normalization order open and names readings for each (header of the claims file); a claim holds when it holds under any combination, and every combination is shown. The readings, as implemented in cancan.py:

  • layout: r2 46 ROIs on each diagonal, averaged; r1-dA and r1-dB 92 ROIs on one diagonal;
  • background: b1 raw counts; b2 "minus a cell-free or dark offset, where the data hold one", run two ways because the files hold no dark frame: b2-dark minus the 1st percentile of the image's nonzero 8 x 8 block means (blocks far outside the array), and b2-min minus the darkest pixel of the full-resolution region around the array;
  • center: c1 the two ROIs nearest the center (equally near, averaged); c2 the highest ROI between the minima on either side;
  • periphery: e1 the mean of the two end ROIs; e2-neg and e2-pos one end;
  • normalization: n1 each dish normalized, then averaged; n2 the mean profile normalized;
  • least affected area: t1 the mean of the two side minima; t2-neg, t2-pos one side.

The first round used the median inside the electrode footprints as b2. The footprints hold a faint monolayer (shown above), so that level is not cell-free; it is shown as the variant b2-fp, and it does not count towards any outcome.

The side minima and the c2 peak are found on the treatment's mean profile. The reading shown first is r2, b1, c1, e1, n1, t1: both diagonals (Fig. 5B legend averages both, and these treatments rotate the packet), no background (none is named in Methods 2.4) and the most literal center and periphery.

In [9]:
fig, axes = plt.subplots(1, 2, figsize=(13, 4.5))
colors = {"1Px4": "k", "5Px4": "tab:blue", "10Px4": "tab:red"}
data = table("r2", "b1")
ranges = []
for t in TREATMENTS:
    pos, P = data[t]
    m, se = P.mean(0), P.std(0, ddof=1) / np.sqrt(len(P))
    axes[0].errorbar(pos, m, se, color=colors[t], label=f"{t} (n={len(P)})", lw=1, capsize=0)
    N = P / cancan.periphery(P, "e1")[:, None] * 100
    mn, sn = N.mean(0), N.std(0, ddof=1) / np.sqrt(len(N))
    axes[1].errorbar(pos, mn, sn, color=colors[t], label=t, lw=1, capsize=0)
    ranges.append({"treatment": t, "au_mean_min": m.min().round(1), "au_mean_max": m.max().round(1),
                   "norm_mean_min": mn.min().round(1), "norm_mean_max": mn.max().round(1)})
axes[0].set_ylabel("YP intensity (counts)"); axes[1].set_ylabel("% of most peripheral ROIs (e1, n1)")
for ax in axes:
    ax.set_xlabel("distance from array center along the diagonal (mm)"); ax.legend()
axes[0].set_title("Reading r2, b1: mean +/- s.e. over dishes")
axes[1].set_title("Normalized as in Fig. 6B")
plt.tight_layout(); plt.show()
print(pd.DataFrame(ranges).to_string(index=False))
print("All points are within the axes.")
No description has been provided for this image
treatment  au_mean_min  au_mean_max  norm_mean_min  norm_mean_max
     1Px4        455.9        748.2           83.7          139.5
     5Px4       1072.6       3783.1           37.3          110.3
    10Px4       1820.2      10193.6           19.9          110.1
All points are within the axes.
In [10]:
# Per-dish profiles, reading r2, b1: raw counts (top) and in percent of the mean of the two ends (bottom).
fig, axes = plt.subplots(2, 3, figsize=(15, 8), sharex=True)
data = table("r2", "b1")
rows = []
for j, t in enumerate(TREATMENTS):
    pos, P = data[t]
    for d, p in zip(dishes[t], P):
        n = p / cancan.periphery(p, "e1")[0] * 100
        axes[0, j].plot(pos, p, lw=1, label=f"dish {d['dish']}")
        axes[1, j].plot(pos, n, lw=1, label=f"dish {d['dish']}")
        ci = cancan.center_index(p, "c1")
        rows.append({"treatment": t, "dish": d["dish"], "counts_min": round(p.min()), "counts_max": round(p.max()),
                     "neg_end": round(p[0]), "center_c1": round(cancan.center_value(p, ci)[0]), "pos_end": round(p[-1]),
                     "pct_min": round(n.min(), 1), "pct_max": round(n.max(), 1)})
    axes[0, j].plot(pos, P.mean(0), "k--", lw=1.5, label="mean")
    axes[1, j].plot(pos, (P / cancan.periphery(P, "e1")[:, None] * 100).mean(0), "k--", lw=1.5, label="mean (n1)")
    axes[0, j].set_title(t); axes[0, j].legend(fontsize=8)
axes[0, 0].set_ylabel("YP intensity (counts)"); axes[1, 0].set_ylabel("% of the mean of the two ends")
for ax in axes[1]:
    ax.set_xlabel("distance from array center (mm)")
plt.tight_layout(); plt.show()
print(pd.DataFrame(rows).to_string(index=False))
print("All points are within the axes.")
No description has been provided for this image
treatment  dish  counts_min  counts_max  neg_end  center_c1  pos_end  pct_min  pct_max
     1Px4    23         480         946      557        799      497     91.2    179.7
     1Px4    24         395         783      615        711      642     62.8    124.7
     1Px4    25         364         715      433        513      524     76.1    149.4
     5Px4     9         932        3477     3181       2518     3048     29.9    111.6
     5Px4    20        1393        6690     3257       3639     6690     28.0    134.5
     5Px4    22         867        2022     1655       1735     1612     53.1    123.7
    10Px4     4        1939        9637     6131       6100     9637     24.6    122.2
    10Px4    11        1281        8419     5618       4502     8419     18.2    120.0
    10Px4    18        1668       15943    15943       4915    12525     11.7    112.0
All points are within the axes.

fig6a-more-packets-more-uptake¶

Increasing the total number of rotated Packet 2 packets from 4 (1Px4) to 20 (5Px4) and 40 (10Px4) profoundly enhanced YP uptake throughout the electrode array.

Criterion (claims file): in a.u., at the periphery, the center and the minimum between them, the mean is ordered 4 < 20 < 40 packets and 40 exceeds 4 by a two-sided Student's t-test on per-dish values at p < 0.05; partially reproduced when the order holds at all three places and the test at one or two. Run under every combination of layout, background, center, periphery and trough readings (the normalization readings do not apply to a.u. values).

In [11]:
rows = []
for lay in cancan.LAYOUTS:
    for bg in cancan.BACKGROUNDS + cancan.VARIANT_BACKGROUNDS:
        data = table(lay, bg)
        for cen in cancan.CENTERS:
            for per in cancan.PERIPHERIES:
                for tro in cancan.TROUGHS:
                    v = {}
                    for t in TREATMENTS:
                        pos, P = data[t]
                        m = P.mean(0)
                        v[t] = {"periphery": cancan.periphery(P, per),
                                "center": cancan.center_value(P, cancan.center_index(m, cen)),
                                "trough": cancan.trough_value(P, cancan.side_minima(m), tro)}
                    row = {"layout": lay, "bg": bg, "center": cen, "periphery": per, "trough": tro}
                    for place in ["periphery", "center", "trough"]:
                        a, b, c = (v[t][place] for t in TREATMENTS)
                        row[f"{place}_means"] = f"{a.mean():.0f} / {b.mean():.0f} / {c.mean():.0f}"
                        row[f"{place}_order"] = a.mean() < b.mean() < c.mean()
                        row[f"{place}_p"] = stats.ttest_ind(c, a).pvalue
                        row[f"{place}_ratio40to4"] = c.mean() / a.mean()
                    rows.append(row)
allf6a = pd.DataFrame(rows)
places = ["periphery", "center", "trough"]
allf6a["order_all"] = allf6a[[f"{p}_order" for p in places]].all(axis=1)
allf6a["n_sig"] = (allf6a[[f"{p}_p" for p in places]] < 0.05).sum(axis=1)
allf6a["outcome"] = np.where(allf6a.order_all & (allf6a.n_sig == 3), "reproduced",
                             np.where(allf6a.order_all & (allf6a.n_sig >= 1), "partially", "not"))
f6a = allf6a[allf6a.bg.isin(cancan.BACKGROUNDS)]
print("Primary reading (r2, b1, c1, e1, t1):")
print(f6a[(f6a.layout == "r2") & (f6a.bg == "b1") & (f6a.center == "c1") & (f6a.periphery == "e1") & (f6a.trough == "t1")].T.to_string())
print("\nReadings of the claims file (backgrounds", cancan.BACKGROUNDS, "):")
print("Combinations by outcome:", f6a.outcome.value_counts().to_dict(), "of", len(f6a))
print("Order 4 < 20 < 40 at all three places in", int(f6a.order_all.sum()), "of", len(f6a))
print("Range over combinations of the 40-over-4 ratio:",
      {p: (round(f6a[f"{p}_ratio40to4"].min(), 2), round(f6a[f"{p}_ratio40to4"].max(), 2)) for p in places})
print("Range over combinations of the t-test p (40 against 4):",
      {p: (float(f6a[f"{p}_p"].min().round(5)), float(f6a[f"{p}_p"].max().round(5))) for p in places})
failed = {p: int(((f6a[f"{p}_p"] >= 0.05) & f6a.order_all).sum()) for p in places}
print("Combinations in which the test fails, by place:", failed)
print("Periphery test failures by layout and periphery reading:")
print(f6a[f6a.periphery_p >= 0.05].groupby(["layout", "periphery"]).size().to_string())
print("\nBy layout and background, combinations reproduced / partially / not:")
print(f6a.groupby(["layout", "bg"]).outcome.value_counts().unstack(fill_value=0).to_string())
v6a = allf6a[allf6a.bg.isin(cancan.VARIANT_BACKGROUNDS)]
print("\nVariant b2-fp (footprint median, not a reading of the claims file): combinations by outcome", v6a.outcome.value_counts().to_dict(), "of", len(v6a))
Primary reading (r2, b1, c1, e1, t1):
                                      0
layout                               r2
bg                                   b1
center                               c1
periphery                            e1
trough                               t1
periphery_means       544 / 3240 / 9712
periphery_order                    True
periphery_p                    0.015737
periphery_ratio40to4          17.838235
center_means          674 / 2631 / 5172
center_order                       True
center_p                       0.000759
center_ratio40to4              7.672547
trough_means          479 / 1148 / 1880
trough_order                       True
trough_p                        0.00079
trough_ratio40to4              3.923182
order_all                          True
n_sig                                 3
outcome                      reproduced

Readings of the claims file (backgrounds ('b1', 'b2-dark', 'b2-min') ):
Combinations by outcome: {'partially': 90, 'reproduced': 72} of 162
Order 4 < 20 < 40 at all three places in 162 of 162
Range over combinations of the 40-over-4 ratio: {'periphery': (np.float64(8.38), np.float64(34.84)), 'center': (np.float64(7.67), np.float64(11.56)), 'trough': (np.float64(3.0), np.float64(4.76))}
Range over combinations of the t-test p (40 against 4): {'periphery': (0.00138, 0.12664), 'center': (0.00043, 0.01188), 'trough': (0.00065, 0.04064)}
Combinations in which the test fails, by place: {'periphery': 90, 'center': 0, 'trough': 0}
Periphery test failures by layout and periphery reading:
layout  periphery
r1-dA   e1           18
        e2-neg       18
        e2-pos       18
r1-dB   e2-neg       18
r2      e2-neg       18

By layout and background, combinations reproduced / partially / not:
outcome         partially  reproduced
layout bg                            
r1-dA  b1              18           0
       b2-dark         18           0
       b2-min          18           0
r1-dB  b1               6          12
       b2-dark          6          12
       b2-min           6          12
r2     b1               6          12
       b2-dark          6          12
       b2-min           6          12

Variant b2-fp (footprint median, not a reading of the claims file): combinations by outcome {'partially': 30, 'reproduced': 24} of 54
In [12]:
# Per dish, primary reading: the values the test uses.
data = table("r2", "b1")
rows = []
for t in TREATMENTS:
    pos, P = data[t]
    m = P.mean(0)
    ci, mins = cancan.center_index(m, "c1"), cancan.side_minima(m)
    for d, p in zip(dishes[t], P):
        rows.append({"treatment": t, "dish": d["dish"], "periphery_e1": round(cancan.periphery(p, "e1")[0], 1),
                     "center_c1": round(cancan.center_value(p, ci)[0], 1), "trough_t1": round(cancan.trough_value(p, mins, "t1")[0], 1),
                     "trough_positions_mm": [round(pos[i], 2) for i in mins]})
print(pd.DataFrame(rows).to_string(index=False))
treatment  dish  periphery_e1  center_c1  trough_t1 trough_positions_mm
     1Px4    23         526.8      798.8      558.7       [-0.54, 0.82]
     1Px4    24         628.2      710.6      480.6       [-0.54, 0.82]
     1Px4    25         478.4      513.0      398.4       [-0.54, 0.82]
     5Px4     9        3114.4     2518.1     1056.4       [-0.75, 0.97]
     5Px4    20        4973.3     3639.4     1457.6       [-0.75, 0.97]
     5Px4    22        1633.8     1735.2      931.2       [-0.75, 0.97]
    10Px4     4        7884.1     6099.8     2090.7       [-0.54, 0.54]
    10Px4    11        7018.4     4502.4     1600.0       [-0.54, 0.54]
    10Px4    18       14233.7     4914.6     1949.8       [-0.54, 0.54]
In [13]:
# Variant: ROI extent from the footprints (no pixel size), primary reading (r2, b1, c1, e1, t1).
data = table("r2", "b1", "geometric")
v = {}
for t in TREATMENTS:
    pos, P = data[t]
    m = P.mean(0)
    v[t] = {"periphery": cancan.periphery(P, "e1"), "center": cancan.center_value(P, cancan.center_index(m, "c1")),
            "trough": cancan.trough_value(P, cancan.side_minima(m), "t1")}
for place in ["periphery", "center", "trough"]:
    a, b, c = (v[t][place] for t in TREATMENTS)
    print(f"{place}: means {a.mean():.0f} / {b.mean():.0f} / {c.mean():.0f}, order {a.mean() < b.mean() < c.mean()}, "
          f"40 against 4 p = {stats.ttest_ind(c, a).pvalue:.4f}")
periphery: means 579 / 2667 / 8851, order True, 40 against 4 p = 0.0351
center: means 673 / 2639 / 5165, order True, 40 against 4 p = 0.0008
trough: means 475 / 1150 / 1899, order True, 40 against 4 p = 0.0008

Result: reproduced. Under the primary reading (both diagonals, raw counts, the two central ROIs, the mean of the two ends, the mean of the two side minima), the mean intensity rises from 4 to 20 to 40 packets at the periphery (544, 3240, 9712 counts), the center (674, 2631, 5172) and the least affected area (479, 1148, 1880), and 40 exceeds 4 at each place by a two-sided Student's t-test on 3 dishes each (p = 0.016, 0.00076 and 0.00079). The order holds in all 162 combinations of the claims file's readings; all three tests pass in 72 and at least one in the other 90. Only the periphery test fails (p up to 0.127): in all 54 combinations on diagonal A alone and in the 36 others that take the periphery from the negative end alone. So the full result needs diagonal B or both diagonals, with the periphery from both ends or the positive end, not from the negative end alone; the other readings give a partial result, and the claims file accepts a claim that holds under any combination. Saturation of up to 5% of an ROI near the 10Px4 electrodes lowers the 10Px4 periphery, so it works against the order. With the ROI extent taken from the footprints the primary reading gives the same order and p = 0.035, 0.0008 and 0.0008. The footprint variant b2-fp, outside the readings, gives 24 reproduced and 30 partial of 54.

fig6b-peak-relative-to-periphery¶

The central peak was almost twofold brighter than the periphery for 1Px4, and about the same as the periphery for 5Px4 and 10Px4.

Criterion (claims file): normalized center 200 +/- 20% for 1Px4 and 100 +/- 10% for 5Px4 and 10Px4; reproduced when all three are within tolerance under one combination of readings; partially reproduced when 1Px4 is within tolerance and above both other treatments.

In [14]:
def fig6b_table(extent):
    rows = []
    for lay in cancan.LAYOUTS:
        for bg in cancan.BACKGROUNDS + cancan.VARIANT_BACKGROUNDS:
            data = table(lay, bg, extent)
            for cen in cancan.CENTERS:
                for per in cancan.PERIPHERIES:
                    for norm in cancan.NORMALIZATIONS:
                        row = {"layout": lay, "bg": bg, "center": cen, "periphery": per, "norm": norm}
                        for t in TREATMENTS:
                            pos, P = data[t]
                            Q = cancan.mean_profile(P, per, norm)
                            ci = cancan.center_index(Q, cen)
                            row[t] = cancan.center_value(Q, ci)[0]
                            if norm == "n1":
                                per_dish = cancan.center_value(P, ci) / cancan.periphery(P, per) * 100
                                row[f"{t}_se"] = per_dish.std(ddof=1) / np.sqrt(len(per_dish))
                        rows.append(row)
    return pd.DataFrame(rows)


def judge_6b(f6b):
    f6b["in_1Px4"] = f6b["1Px4"].between(180, 220)
    f6b["in_5Px4"] = f6b["5Px4"].between(90, 110)
    f6b["in_10Px4"] = f6b["10Px4"].between(90, 110)
    f6b["1Px4_highest"] = (f6b["1Px4"] > f6b["5Px4"]) & (f6b["1Px4"] > f6b["10Px4"])
    f6b["outcome"] = np.where(f6b.in_1Px4 & f6b.in_5Px4 & f6b.in_10Px4, "reproduced",
                              np.where(f6b.in_1Px4 & f6b["1Px4_highest"], "partially", "not"))
    return f6b


allf6b = judge_6b(fig6b_table("camera"))
f6b = allf6b[allf6b.bg.isin(cancan.BACKGROUNDS)]
print(f6b.round(1).to_string(index=False))
print("\nReadings of the claims file (backgrounds", cancan.BACKGROUNDS, "): combinations by outcome", f6b.outcome.value_counts().to_dict(), "of", len(f6b))
for t in TREATMENTS:
    print(f"{t}: normalized center {f6b[t].min():.1f} to {f6b[t].max():.1f}% over combinations, median {f6b[t].median():.1f}%")
print("1Px4 above both others in", int(f6b["1Px4_highest"].sum()), "of", len(f6b))
print("Within tolerance: 1Px4 in", int(f6b.in_1Px4.sum()), "; 5Px4 in", int(f6b.in_5Px4.sum()), "; 10Px4 in", int(f6b.in_10Px4.sum()))
print("\nCombinations that are reproduced or partially reproduced:")
print(f6b[f6b.outcome != "not"].round(1).to_string(index=False))
print("\nBy background, combinations by outcome:", allf6b.groupby("bg").outcome.value_counts().unstack(fill_value=0).to_dict("index"))
print("By layout and center, partial combinations:", f6b[f6b.outcome == "partially"].groupby(["layout", "center"]).size().to_dict())
g6b = judge_6b(fig6b_table("geometric"))
gv = g6b[g6b.bg.isin(cancan.BACKGROUNDS)]
print("\nVariant, ROI extent from the footprints (no pixel size): combinations by outcome", gv.outcome.value_counts().to_dict(), "of", len(gv),
      "; by background", g6b.groupby("bg").outcome.value_counts().unstack(fill_value=0).to_dict("index"))
for t in TREATMENTS:
    print(f"  {t}: {gv[t].min():.1f} to {gv[t].max():.1f}%, median {gv[t].median():.1f}%; within tolerance in", int(gv[f"in_{t}"].sum()))
print("  primary reading (r2, b1, c1, e1, n1):", gv[(gv.layout == "r2") & (gv.bg == "b1") & (gv.center == "c1") & (gv.periphery == "e1") & (gv.norm == "n1")][TREATMENTS].round(1).to_dict("records"))
layout      bg center periphery norm  1Px4  1Px4_se  5Px4  5Px4_se  10Px4  10Px4_se  in_1Px4  in_5Px4  in_10Px4  1Px4_highest   outcome
    r2      b1     c1        e1   n1 124.0     13.9  86.7     10.0   58.7      12.7    False    False     False          True       not
    r2      b1     c1        e1   n2 123.8      NaN  81.2      NaN   53.3       NaN    False    False     False          True       not
    r2      b1     c1    e2-neg   n1 125.8      8.8  98.6      9.9   70.2      20.4    False     True     False          True       not
    r2      b1     c1    e2-neg   n2 126.0      NaN  97.5      NaN   56.0       NaN    False     True     False          True       not
    r2      b1     c1    e2-pos   n1 123.2     19.2  81.6     15.4   52.0       7.0    False    False     False          True       not
    r2      b1     c1    e2-pos   n2 121.7      NaN  69.5      NaN   50.7       NaN    False    False     False          True       not
    r2      b1     c2        e1   n1 139.5     21.9  91.7     13.0   70.8      17.2    False     True     False          True       not
    r2      b1     c2        e1   n2 137.4      NaN  86.1      NaN   63.3       NaN    False    False     False          True       not
    r2      b1     c2    e2-neg   n1 141.7     18.6 105.2     16.3   85.2      26.6    False     True     False          True       not
    r2      b1     c2    e2-neg   n2 139.9      NaN 103.5      NaN   66.6       NaN    False     True     False          True       not
    r2      b1     c2    e2-pos   n1 138.5     26.7  85.6     17.1   62.3      10.3    False    False     False          True       not
    r2      b1     c2    e2-pos   n2 135.0      NaN  73.8      NaN   60.3       NaN    False    False     False          True       not
    r2 b2-dark     c1        e1   n1 127.9     16.2  86.5     10.2   58.3      12.7    False    False     False          True       not
    r2 b2-dark     c1        e1   n2 127.8      NaN  80.7      NaN   52.9       NaN    False    False     False          True       not
    r2 b2-dark     c1    e2-neg   n1 130.2     10.0  98.6     10.2   69.9      20.5    False     True     False          True       not
    r2 b2-dark     c1    e2-neg   n2 130.5      NaN  97.4      NaN   55.6       NaN    False     True     False          True       not
    r2 b2-dark     c1    e2-pos   n1 127.1     22.5  81.3     15.6   51.6       7.0    False    False     False          True       not
    r2 b2-dark     c1    e2-pos   n2 125.3      NaN  68.9      NaN   50.3       NaN    False    False     False          True       not
    r2 b2-dark     c2        e1   n1 146.2     25.5  91.6     13.4   70.6      17.3    False     True     False          True       not
    r2 b2-dark     c2        e1   n2 143.8      NaN  85.8      NaN   63.0       NaN    False    False     False          True       not
    r2 b2-dark     c2    e2-neg   n1 149.1     21.7 105.5     16.8   85.2      26.8    False     True     False          True       not
    r2 b2-dark     c2    e2-neg   n2 146.8      NaN 103.6      NaN   66.3       NaN    False     True     False          True       not
    r2 b2-dark     c2    e2-pos   n1 145.1     31.4  85.6     17.5   62.0      10.4    False    False     False          True       not
    r2 b2-dark     c2    e2-pos   n2 140.8      NaN  73.2      NaN   60.0       NaN    False    False     False          True       not
    r2  b2-min     c1        e1   n1 130.3     17.6  86.4     10.5   57.9      12.7    False    False     False          True       not
    r2  b2-min     c1        e1   n2 130.0      NaN  80.3      NaN   52.3       NaN    False    False     False          True       not
    r2  b2-min     c1    e2-neg   n1 132.7     10.8  98.5     10.5   69.7      20.6    False     True     False          True       not
    r2  b2-min     c1    e2-neg   n2 133.0      NaN  97.4      NaN   55.1       NaN    False     True     False          True       not
    r2  b2-min     c1    e2-pos   n1 129.6     24.7  81.2     15.9   51.1       7.0    False    False     False          True       not
    r2  b2-min     c1    e2-pos   n2 127.2      NaN  68.3      NaN   49.8       NaN    False    False     False          True       not
    r2  b2-min     c2        e1   n1 150.2     27.7  91.6     13.9   70.3      17.3    False     True     False          True       not
    r2  b2-min     c2        e1   n2 147.2      NaN  85.5      NaN   62.6       NaN    False    False     False          True       not
    r2  b2-min     c2    e2-neg   n1 153.5     23.6 105.6     17.3   85.1      27.0    False     True     False          True       not
    r2  b2-min     c2    e2-neg   n2 150.5      NaN 103.7      NaN   65.9       NaN    False     True     False          True       not
    r2  b2-min     c2    e2-pos   n1 149.2     34.4  85.6     18.0   61.7      10.4    False    False     False          True       not
    r2  b2-min     c2    e2-pos   n2 144.0      NaN  72.7      NaN   59.6       NaN    False    False     False          True       not
 r1-dA      b1     c1        e1   n1 137.7     24.2 112.2     26.1  101.0      57.6    False    False      True          True       not
 r1-dA      b1     c1        e1   n2 135.7      NaN 102.2      NaN   61.7       NaN    False     True     False          True       not
 r1-dA      b1     c1    e2-neg   n1 136.8     22.7  97.9     20.4  179.7      61.9    False     True     False         False       not
 r1-dA      b1     c1    e2-neg   n2 136.2      NaN  80.0      NaN  126.5       NaN    False    False     False          True       not
 r1-dA      b1     c1    e2-pos   n1 141.1     27.4 154.5     53.5   78.4      50.9    False    False     False         False       not
 r1-dA      b1     c1    e2-pos   n2 135.1      NaN 141.1      NaN   40.8       NaN    False    False     False         False       not
 r1-dA      b1     c2        e1   n1 223.1    124.8 123.6     13.2  164.0     108.5    False    False     False          True       not
 r1-dA      b1     c2        e1   n2 173.9      NaN 115.5      NaN   90.2       NaN    False    False      True          True       not
 r1-dA      b1     c2    e2-neg   n1 177.7     56.9 113.1     40.0  273.5     115.0    False    False     False         False       not
 r1-dA      b1     c2    e2-neg   n2 174.6      NaN  90.5      NaN  185.1       NaN    False     True     False         False       not
 r1-dA      b1     c2    e2-pos   n1 242.9    146.7 162.8     30.9  129.2      94.2    False    False     False          True       not
 r1-dA      b1     c2    e2-pos   n2 173.2      NaN 159.6      NaN   59.6       NaN    False    False     False          True       not
 r1-dA b2-dark     c1        e1   n1 144.8     28.8 112.8     27.0  102.0      59.0    False    False      True          True       not
 r1-dA b2-dark     c1        e1   n2 142.3      NaN 102.2      NaN   61.3       NaN    False     True     False          True       not
 r1-dA b2-dark     c1    e2-neg   n1 143.5     26.8  98.3     20.9  183.1      63.9    False     True     False         False       not
 r1-dA b2-dark     c1    e2-neg   n2 143.0      NaN  79.6      NaN  127.0       NaN    False    False     False          True       not
 r1-dA b2-dark     c1    e2-pos   n1 149.7     32.7 157.6     56.8   78.9      51.7    False    False     False         False       not
 r1-dA b2-dark     c1    e2-pos   n2 141.6      NaN 142.7      NaN   40.4       NaN    False    False     False         False       not
 r1-dA b2-dark     c2        e1   n1 249.3    151.0 124.7     13.8  166.7     111.6    False    False     False          True       not
 r1-dA b2-dark     c2        e1   n2 187.6      NaN 115.9      NaN   90.1       NaN     True    False      True          True partially
 r1-dA b2-dark     c2    e2-neg   n1 191.8     67.1 114.8     42.3  280.4     119.3     True    False     False         False       not
 r1-dA b2-dark     c2    e2-neg   n2 188.5      NaN  90.3      NaN  186.8       NaN     True     True     False          True partially
 r1-dA b2-dark     c2    e2-pos   n1 278.7    182.3 165.9     33.0  130.9      96.2    False    False     False          True       not
 r1-dA b2-dark     c2    e2-pos   n2 186.7      NaN 161.9      NaN   59.4       NaN     True    False     False          True partially
 r1-dA  b2-min     c1        e1   n1 149.3     31.9 113.7     28.2  103.1      60.6    False    False      True          True       not
 r1-dA  b2-min     c1        e1   n2 145.9      NaN 102.3      NaN   60.8       NaN    False     True     False          True       not
 r1-dA  b2-min     c1    e2-neg   n1 147.5     29.5  98.9     21.3  187.7      66.6    False     True     False         False       not
 r1-dA  b2-min     c1    e2-neg   n2 146.7      NaN  79.2      NaN  127.7       NaN    False    False     False          True       not
 r1-dA  b2-min     c1    e2-pos   n1 155.2     36.2 162.0     61.5   79.4      52.7    False    False     False         False       not
 r1-dA  b2-min     c1    e2-pos   n2 145.2      NaN 144.5      NaN   39.9       NaN    False    False     False          True       not
 r1-dA  b2-min     c2        e1   n1 265.0    166.3 126.1     14.5  170.0     115.3    False    False     False          True       not
 r1-dA  b2-min     c2        e1   n2 195.1      NaN 116.4      NaN   90.0       NaN     True    False     False          True partially
 r1-dA  b2-min     c2    e2-neg   n1 200.7     73.8 117.2     45.3  289.6     124.7     True    False     False         False       not
 r1-dA  b2-min     c2    e2-neg   n2 196.1      NaN  90.1      NaN  189.0       NaN     True     True     False          True partially
 r1-dA  b2-min     c2    e2-pos   n1 301.3    204.2 170.0     36.0  132.9      98.6    False    False     False          True       not
 r1-dA  b2-min     c2    e2-pos   n2 194.1      NaN 164.4      NaN   59.1       NaN     True    False     False          True partially
 r1-dB      b1     c1        e1   n1 131.4     18.8  54.7      2.2   56.3      17.0    False    False     False          True       not
 r1-dB      b1     c1        e1   n2 130.7      NaN  54.6      NaN   48.5       NaN    False    False     False          True       not
 r1-dB      b1     c1    e2-neg   n1 135.3     12.7  82.2     21.2   52.2      20.2    False    False     False          True       not
 r1-dB      b1     c1    e2-neg   n2 134.5      NaN  84.0      NaN   40.4       NaN    False    False     False          True       not
 r1-dB      b1     c1    e2-pos   n1 129.9     26.2  46.9      9.3   63.9      12.5    False    False     False          True       not
 r1-dB      b1     c1    e2-pos   n2 127.1      NaN  40.4      NaN   60.6       NaN    False    False     False          True       not
 r1-dB      b1     c2        e1   n1 167.7     31.9  73.9     24.7   93.4      31.8    False    False      True          True       not
 r1-dB      b1     c2        e1   n2 166.5      NaN  79.8      NaN   77.1       NaN    False    False     False          True       not
 r1-dB      b1     c2    e2-neg   n1 172.6     27.5 120.8     65.6   86.9      35.6    False    False     False          True       not
 r1-dB      b1     c2    e2-neg   n2 171.3      NaN 122.9      NaN   64.3       NaN    False    False     False          True       not
 r1-dB      b1     c2    e2-pos   n1 166.6     41.4  63.6     18.6  105.2      27.2    False    False      True          True       not
 r1-dB      b1     c2    e2-pos   n2 161.9      NaN  59.1      NaN   96.4       NaN    False    False      True          True       not
 r1-dB b2-dark     c1        e1   n1 137.0     22.0  53.7      2.1   56.0      17.1    False    False     False          True       not
 r1-dB b2-dark     c1        e1   n2 136.3      NaN  53.7      NaN   48.1       NaN    False    False     False          True       not
 r1-dB b2-dark     c1    e2-neg   n1 142.2     14.7  81.6     21.9   51.9      20.2    False    False     False          True       not
 r1-dB b2-dark     c1    e2-neg   n2 141.0      NaN  83.6      NaN   40.1       NaN    False    False     False          True       not
 r1-dB b2-dark     c1    e2-pos   n1 135.5     31.2  46.0      9.0   63.5      12.5    False    False     False          True       not
 r1-dB b2-dark     c1    e2-pos   n2 132.0      NaN  39.6      NaN   60.3       NaN    False    False     False          True       not
 r1-dB b2-dark     c2        e1   n1 180.0     37.3  73.3     25.1   93.5      32.0    False    False      True          True       not
 r1-dB b2-dark     c2        e1   n2 178.6      NaN  79.4      NaN   77.0       NaN    False    False     False          True       not
 r1-dB b2-dark     c2    e2-neg   n1 188.8     36.1 121.4     67.4   87.0      35.8     True    False     False          True partially
 r1-dB b2-dark     c2    e2-neg   n2 184.8      NaN 123.5      NaN   64.1       NaN     True    False     False          True partially
 r1-dB b2-dark     c2    e2-pos   n1 179.0     49.5  63.5     40.6  105.3      27.4    False    False      True          True       not
 r1-dB b2-dark     c2    e2-pos   n2 172.9      NaN  58.5      NaN   96.4       NaN    False    False      True          True       not
 r1-dB  b2-min     c1        e1   n1 140.5     24.2  52.6      2.2   55.5      17.2    False    False     False          True       not
 r1-dB  b2-min     c1        e1   n2 139.4      NaN  52.9      NaN   47.7       NaN    False    False     False          True       not
 r1-dB  b2-min     c1    e2-neg   n1 146.4     16.1  80.8     22.5   51.5      20.3    False    False     False          True       not
 r1-dB  b2-min     c1    e2-neg   n2 144.6      NaN  83.1      NaN   39.6       NaN    False    False     False          True       not
 r1-dB  b2-min     c1    e2-pos   n1 139.1     34.5  44.9      8.6   63.1      12.6    False    False     False          True       not
 r1-dB  b2-min     c1    e2-pos   n2 134.6      NaN  38.8      NaN   59.8       NaN    False    False     False          True       not
 r1-dB  b2-min     c2        e1   n1 187.3     41.0  72.4     25.6   93.6      32.3     True    False      True          True partially
 r1-dB  b2-min     c2        e1   n2 185.3      NaN  79.0      NaN   76.8       NaN     True    False     False          True partially
 r1-dB  b2-min     c2    e2-neg   n1 198.7     41.7 121.3     68.9   87.0      36.1     True    False     False          True partially
 r1-dB  b2-min     c2    e2-neg   n2 192.3      NaN 124.2      NaN   63.8       NaN     True    False     False          True partially
 r1-dB  b2-min     c2    e2-pos   n1 186.6     54.9  63.7     41.7  105.5      27.7     True    False      True          True partially
 r1-dB  b2-min     c2    e2-pos   n2 178.9      NaN  58.0      NaN   96.3       NaN    False    False      True          True       not

Readings of the claims file (backgrounds ('b1', 'b2-dark', 'b2-min') ): combinations by outcome {'not': 95, 'partially': 13} of 108
1Px4: normalized center 121.7 to 301.3% over combinations, median 144.7%
5Px4: normalized center 38.8 to 170.0% over combinations, median 88.4%
10Px4: normalized center 39.6 to 289.6% over combinations, median 65.1%
1Px4 above both others in 96 of 108
Within tolerance: 1Px4 in 15 ; 5Px4 in 24 ; 10Px4 in 14

Combinations that are reproduced or partially reproduced:
layout      bg center periphery norm  1Px4  1Px4_se  5Px4  5Px4_se  10Px4  10Px4_se  in_1Px4  in_5Px4  in_10Px4  1Px4_highest   outcome
 r1-dA b2-dark     c2        e1   n2 187.6      NaN 115.9      NaN   90.1       NaN     True    False      True          True partially
 r1-dA b2-dark     c2    e2-neg   n2 188.5      NaN  90.3      NaN  186.8       NaN     True     True     False          True partially
 r1-dA b2-dark     c2    e2-pos   n2 186.7      NaN 161.9      NaN   59.4       NaN     True    False     False          True partially
 r1-dA  b2-min     c2        e1   n2 195.1      NaN 116.4      NaN   90.0       NaN     True    False     False          True partially
 r1-dA  b2-min     c2    e2-neg   n2 196.1      NaN  90.1      NaN  189.0       NaN     True     True     False          True partially
 r1-dA  b2-min     c2    e2-pos   n2 194.1      NaN 164.4      NaN   59.1       NaN     True    False     False          True partially
 r1-dB b2-dark     c2    e2-neg   n1 188.8     36.1 121.4     67.4   87.0      35.8     True    False     False          True partially
 r1-dB b2-dark     c2    e2-neg   n2 184.8      NaN 123.5      NaN   64.1       NaN     True    False     False          True partially
 r1-dB  b2-min     c2        e1   n1 187.3     41.0  72.4     25.6   93.6      32.3     True    False      True          True partially
 r1-dB  b2-min     c2        e1   n2 185.3      NaN  79.0      NaN   76.8       NaN     True    False     False          True partially
 r1-dB  b2-min     c2    e2-neg   n1 198.7     41.7 121.3     68.9   87.0      36.1     True    False     False          True partially
 r1-dB  b2-min     c2    e2-neg   n2 192.3      NaN 124.2      NaN   63.8       NaN     True    False     False          True partially
 r1-dB  b2-min     c2    e2-pos   n1 186.6     54.9  63.7     41.7  105.5      27.7     True    False      True          True partially

By background, combinations by outcome: {'b1': {'not': 36, 'partially': 0}, 'b2-dark': {'not': 31, 'partially': 5}, 'b2-fp': {'not': 29, 'partially': 7}, 'b2-min': {'not': 28, 'partially': 8}}
By layout and center, partial combinations: {('r1-dA', 'c2'): 6, ('r1-dB', 'c2'): 7}

Variant, ROI extent from the footprints (no pixel size): combinations by outcome {'not': 107, 'partially': 1} of 108 ; by background {'b1': {'not': 35, 'partially': 1}, 'b2-dark': {'not': 36, 'partially': 0}, 'b2-fp': {'not': 34, 'partially': 2}, 'b2-min': {'not': 36, 'partially': 0}}
  1Px4: 104.9 to 284.8%, median 131.8%; within tolerance in 1
  5Px4: 66.7 to 257.3%, median 112.6%; within tolerance in 36
  10Px4: 47.5 to 166.9%, median 80.4%; within tolerance in 12
  primary reading (r2, b1, c1, e1, n1): [{'1Px4': 116.1, '5Px4': 105.1, '10Px4': 68.0}]
In [15]:
# Per dish, primary reading (r2, c1, e1) under each background.
rows = []
for bg in cancan.BACKGROUNDS + cancan.VARIANT_BACKGROUNDS:
    data = table("r2", bg)
    for t in TREATMENTS:
        pos, P = data[t]
        ci = cancan.center_index(P.mean(0), "c1")
        for d, p in zip(dishes[t], P):
            rows.append({"bg": bg, "treatment": t, "dish": d["dish"],
                         "center_pct_of_periphery": round(float(cancan.center_value(p, ci)[0] / cancan.periphery(p, "e1")[0] * 100), 1),
                         "center_pct_neg_end": round(float(cancan.center_value(p, ci)[0] / p[0] * 100), 1),
                         "center_pct_pos_end": round(float(cancan.center_value(p, ci)[0] / p[-1] * 100), 1)})
print(pd.DataFrame(rows).to_string(index=False))

# Per dish, every partial combination: the center ROI is the one the combination reads from the mean profile.
print("\nPartial combinations, per dish (counts after the background is subtracted; the percent is per dish, before averaging):")
rows = []
for _, r in f6b[f6b.outcome == "partially"].iterrows():
    data = table(r.layout, r.bg)
    for t in TREATMENTS:
        pos, P = data[t]
        ci = cancan.center_index(cancan.mean_profile(P, r.periphery, r.norm), r.center)
        for d, p in zip(dishes[t], P):
            c, e = cancan.center_value(p, ci)[0], cancan.periphery(p, r.periphery)[0]
            rows.append({"combination": f"{r.layout} {r.bg} {r.center} {r.periphery} {r.norm}", "treatment": t, "dish": d["dish"],
                         "center_pos_mm": round(float(np.mean(pos[ci])), 2), "center": round(c), "periphery": round(e), "pct": round(100 * c / e, 1),
                         "combination_value": round(r[t], 1)})
pdish = pd.DataFrame(rows)
print(pdish.to_string(index=False))
one = pdish[pdish.treatment == "1Px4"]
print("\n1Px4 per-dish percent over the partial combinations:", one.groupby("dish").pct.agg(["min", "median", "max"]).round(1).to_dict("index"))

# How the 1Px4 value depends on the size of the offset: one offset for every dish, from 0 to 300 counts.
print("\n1Px4 normalized center against a common offset subtracted from every dish (r2 c1 e1 n1, and the partial layouts with c2):")
offs = np.arange(0, 301, 25)
sweep = {}
for lay, cen, per, norm in [("r2", "c1", "e1", "n1"), ("r2", "c2", "e1", "n1"), ("r1-dA", "c2", "e1", "n2"), ("r1-dB", "c2", "e1", "n1")]:
    _, P = table(lay, "b1")["1Px4"]
    vals = []
    for o in offs:
        Q = cancan.mean_profile(P - o, per, norm)
        vals.append(round(cancan.center_value(Q, cancan.center_index(Q, cen))[0], 1))
    sweep[f"{lay} {cen} {per} {norm}"] = vals
print(pd.DataFrame(sweep, index=pd.Index(offs, name="offset")).T.to_string())
     bg treatment  dish  center_pct_of_periphery  center_pct_neg_end  center_pct_pos_end
     b1      1Px4    23                    151.6               143.4               160.8
     b1      1Px4    24                    113.1               115.6               110.7
     b1      1Px4    25                    107.2               118.5                97.9
     b1      5Px4     9                     80.9                79.2                82.6
     b1      5Px4    20                     73.2               111.7                54.4
     b1      5Px4    22                    106.2               104.8               107.6
     b1     10Px4     4                     77.4                99.5                63.3
     b1     10Px4    11                     64.2                80.1                53.5
     b1     10Px4    18                     34.5                30.8                39.2
b2-dark      1Px4    23                    160.0               150.0               171.4
b2-dark      1Px4    24                    115.2               118.2               112.4
b2-dark      1Px4    25                    108.6               122.4                97.6
b2-dark      5Px4     9                     80.4                78.6                82.2
b2-dark      5Px4    20                     72.7               112.0                53.9
b2-dark      5Px4    22                    106.5               105.0               108.0
b2-dark     10Px4     4                     77.1                99.5                63.0
b2-dark     10Px4    11                     63.7                79.9                53.0
b2-dark     10Px4    18                     34.2                30.5                38.8
 b2-min      1Px4    23                    165.3               154.1               178.1
 b2-min      1Px4    24                    116.2               119.4               113.1
 b2-min      1Px4    25                    109.4               124.7                97.4
 b2-min      5Px4     9                     79.7                78.0                81.6
 b2-min      5Px4    20                     72.5               112.2                53.5
 b2-min      5Px4    22                    106.9               105.3               108.5
 b2-min     10Px4     4                     76.8                99.5                62.6
 b2-min     10Px4    11                     63.1                79.4                52.3
 b2-min     10Px4    18                     33.7                30.1                38.4
  b2-fp      1Px4    23                    217.5               192.4               250.0
  b2-fp      1Px4    24                    122.8               127.6               118.3
  b2-fp      1Px4    25                    114.4               141.1                96.2
  b2-fp      5Px4     9                     78.4                76.6                80.4
  b2-fp      5Px4    20                     71.4               113.0                52.2
  b2-fp      5Px4    22                    107.6               105.9               109.3
  b2-fp     10Px4     4                     76.4                99.5                62.0
  b2-fp     10Px4    11                     62.0                78.6                51.1
  b2-fp     10Px4    18                     32.8                29.2                37.4

Partial combinations, per dish (counts after the background is subtracted; the percent is per dish, before averaging):
               combination treatment  dish  center_pos_mm  center  periphery   pct  combination_value
    r1-dA b2-dark c2 e1 n2      1Px4    23           0.13    1315        390 337.2              187.6
    r1-dA b2-dark c2 e1 n2      1Px4    24           0.13     460        527  87.4              187.6
    r1-dA b2-dark c2 e1 n2      1Px4    25           0.13     622        362 172.0              187.6
    r1-dA b2-dark c2 e1 n2      5Px4     9           0.13    1270       1992  63.8              115.9
    r1-dA b2-dark c2 e1 n2      5Px4    20           0.13    6305       4840 130.3              115.9
    r1-dA b2-dark c2 e1 n2      5Px4    22           0.13    1957       1390 140.8              115.9
    r1-dA b2-dark c2 e1 n2     10Px4     4           0.05   10796       2771 389.6               90.1
    r1-dA b2-dark c2 e1 n2     10Px4    11           0.05    6280       9750  64.4               90.1
    r1-dA b2-dark c2 e1 n2     10Px4    18           0.05    6077      13170  46.1               90.1
r1-dA b2-dark c2 e2-neg n2      1Px4    23           0.13    1315        407 323.0              188.5
r1-dA b2-dark c2 e2-neg n2      1Px4    24           0.13     460        452 101.9              188.5
r1-dA b2-dark c2 e2-neg n2      1Px4    25           0.13     622        413 150.5              188.5
r1-dA b2-dark c2 e2-neg n2      5Px4     9           0.13    1270       2748  46.2               90.3
r1-dA b2-dark c2 e2-neg n2      5Px4    20           0.13    6305       6700  94.1               90.3
r1-dA b2-dark c2 e2-neg n2      5Px4    22           0.13    1957       1108 176.6               90.3
r1-dA b2-dark c2 e2-neg n2     10Px4     4           0.05   10796       2203 490.1              186.8
r1-dA b2-dark c2 e2-neg n2     10Px4    11           0.05    6280       2291 274.1              186.8
r1-dA b2-dark c2 e2-neg n2     10Px4    18           0.05    6077       7901  76.9              186.8
r1-dA b2-dark c2 e2-pos n2      1Px4    23           0.13    1315        373 352.7              186.7
r1-dA b2-dark c2 e2-pos n2      1Px4    24           0.13     460        602  76.5              186.7
r1-dA b2-dark c2 e2-pos n2      1Px4    25           0.13     622        310 200.7              186.7
r1-dA b2-dark c2 e2-pos n2      5Px4     9           0.13    1270       1236 102.8              161.9
r1-dA b2-dark c2 e2-pos n2      5Px4    20           0.13    6305       2980 211.6              161.9
r1-dA b2-dark c2 e2-pos n2      5Px4    22           0.13    1957       1671 117.1              161.9
r1-dA b2-dark c2 e2-pos n2     10Px4     4           0.05   10796       3340 323.3               59.4
r1-dA b2-dark c2 e2-pos n2     10Px4    11           0.05    6280      17210  36.5               59.4
r1-dA b2-dark c2 e2-pos n2     10Px4    18           0.05    6077      18439  33.0               59.4
     r1-dA b2-min c2 e1 n2      1Px4    23           0.13    1279        354 361.7              195.1
     r1-dA b2-min c2 e1 n2      1Px4    24           0.13     429        495  86.6              195.1
     r1-dA b2-min c2 e1 n2      1Px4    25           0.13     588        328 179.4              195.1
     r1-dA b2-min c2 e1 n2      5Px4     9           0.13    1176       1898  62.0              116.4
     r1-dA b2-min c2 e1 n2      5Px4    20           0.13    6255       4789 130.6              116.4
     r1-dA b2-min c2 e1 n2      5Px4    22           0.13    1871       1304 143.5              116.4
     r1-dA b2-min c2 e1 n2     10Px4     4           0.05   10697       2672 400.3               90.0
     r1-dA b2-min c2 e1 n2     10Px4    11           0.05    6161       9631  64.0               90.0
     r1-dA b2-min c2 e1 n2     10Px4    18           0.05    5985      13079  45.8               90.0
 r1-dA b2-min c2 e2-neg n2      1Px4    23           0.13    1279        371 345.0              196.1
 r1-dA b2-min c2 e2-neg n2      1Px4    24           0.13     429        420 102.0              196.1
 r1-dA b2-min c2 e2-neg n2      1Px4    25           0.13     588        380 155.0              196.1
 r1-dA b2-min c2 e2-neg n2      5Px4     9           0.13    1176       2654  44.3               90.1
 r1-dA b2-min c2 e2-neg n2      5Px4    20           0.13    6255       6649  94.1               90.1
 r1-dA b2-min c2 e2-neg n2      5Px4    22           0.13    1871       1022 183.1               90.1
 r1-dA b2-min c2 e2-neg n2     10Px4     4           0.05   10697       2104 508.5              189.0
 r1-dA b2-min c2 e2-neg n2     10Px4    11           0.05    6161       2172 283.7              189.0
 r1-dA b2-min c2 e2-neg n2     10Px4    18           0.05    5985       7809  76.6              189.0
 r1-dA b2-min c2 e2-pos n2      1Px4    23           0.13    1279        336 380.1              194.1
 r1-dA b2-min c2 e2-pos n2      1Px4    24           0.13     429        570  75.2              194.1
 r1-dA b2-min c2 e2-pos n2      1Px4    25           0.13     588        276 213.0              194.1
 r1-dA b2-min c2 e2-pos n2      5Px4     9           0.13    1176       1142 103.0              164.4
 r1-dA b2-min c2 e2-pos n2      5Px4    20           0.13    6255       2930 213.5              164.4
 r1-dA b2-min c2 e2-pos n2      5Px4    22           0.13    1871       1585 118.0              164.4
 r1-dA b2-min c2 e2-pos n2     10Px4     4           0.05   10697       3241 330.1               59.1
 r1-dA b2-min c2 e2-pos n2     10Px4    11           0.05    6161      17091  36.0               59.1
 r1-dA b2-min c2 e2-pos n2     10Px4    18           0.05    5985      18348  32.6               59.1
r1-dB b2-dark c2 e2-neg n1      1Px4    23           0.23     777        457 170.2              188.8
r1-dB b2-dark c2 e2-neg n1      1Px4    24           0.23     696        505 137.7              188.8
r1-dB b2-dark c2 e2-neg n1      1Px4    25           0.23     744        288 258.4              188.8
r1-dB b2-dark c2 e2-neg n1      5Px4     9          -0.13    1342       3039  44.2              121.4
r1-dB b2-dark c2 e2-neg n1      5Px4    20          -0.13    7321       2862 255.8              121.4
r1-dB b2-dark c2 e2-neg n1      5Px4    22          -0.13    1491       2320  64.3              121.4
r1-dB b2-dark c2 e2-neg n1     10Px4     4           0.16   11917       7894 151.0               87.0
r1-dB b2-dark c2 e2-neg n1     10Px4    11           0.16    7663       9226  83.1               87.0
r1-dB b2-dark c2 e2-neg n1     10Px4    18           0.16    6256      23199  27.0               87.0
r1-dB b2-dark c2 e2-neg n2      1Px4    23           0.16    1073        457 234.9              184.8
r1-dB b2-dark c2 e2-neg n2      1Px4    24           0.16     719        505 142.2              184.8
r1-dB b2-dark c2 e2-neg n2      1Px4    25           0.16     518        288 179.9              184.8
r1-dB b2-dark c2 e2-neg n2      5Px4     9          -0.13    1342       3039  44.2              123.5
r1-dB b2-dark c2 e2-neg n2      5Px4    20          -0.13    7321       2862 255.8              123.5
r1-dB b2-dark c2 e2-neg n2      5Px4    22          -0.13    1491       2320  64.3              123.5
r1-dB b2-dark c2 e2-neg n2     10Px4     4           0.16   11917       7894 151.0               64.1
r1-dB b2-dark c2 e2-neg n2     10Px4    11           0.16    7663       9226  83.1               64.1
r1-dB b2-dark c2 e2-neg n2     10Px4    18           0.16    6256      23199  27.0               64.1
     r1-dB b2-min c2 e1 n1      1Px4    23           0.16    1036        385 269.1              187.3
     r1-dB b2-min c2 e1 n1      1Px4    24           0.16     688        487 141.1              187.3
     r1-dB b2-min c2 e1 n1      1Px4    25           0.16     484        319 151.8              187.3
     r1-dB b2-min c2 e1 n1      5Px4     9          -0.13    1248       4386  28.5               72.4
     r1-dB b2-min c2 e1 n1      5Px4    20          -0.13    7270       6210 117.1               72.4
     r1-dB b2-min c2 e1 n1      5Px4    22          -0.13    1405       1960  71.7               72.4
     r1-dB b2-min c2 e1 n1     10Px4     4           0.16   11818       8074 146.4               93.6
     r1-dB b2-min c2 e1 n1     10Px4    11           0.16    7544       7598  99.3               93.6
     r1-dB b2-min c2 e1 n1     10Px4    18           0.16    6165      17586  35.1               93.6
     r1-dB b2-min c2 e1 n2      1Px4    23           0.16    1036        385 269.1              185.3
     r1-dB b2-min c2 e1 n2      1Px4    24           0.16     688        487 141.1              185.3
     r1-dB b2-min c2 e1 n2      1Px4    25           0.16     484        319 151.8              185.3
     r1-dB b2-min c2 e1 n2      5Px4     9          -0.13    1248       4386  28.5               79.0
     r1-dB b2-min c2 e1 n2      5Px4    20          -0.13    7270       6210 117.1               79.0
     r1-dB b2-min c2 e1 n2      5Px4    22          -0.13    1405       1960  71.7               79.0
     r1-dB b2-min c2 e1 n2     10Px4     4           0.16   11818       8074 146.4               76.8
     r1-dB b2-min c2 e1 n2     10Px4    11           0.16    7544       7598  99.3               76.8
     r1-dB b2-min c2 e1 n2     10Px4    18           0.16    6165      17586  35.1               76.8
 r1-dB b2-min c2 e2-neg n1      1Px4    23           0.23     741        420 176.2              198.7
 r1-dB b2-min c2 e2-neg n1      1Px4    24           0.23     665        474 140.2              198.7
 r1-dB b2-min c2 e2-neg n1      1Px4    25           0.23     710        254 279.5              198.7
 r1-dB b2-min c2 e2-neg n1      5Px4     9          -0.13    1248       2945  42.4              121.3
 r1-dB b2-min c2 e2-neg n1      5Px4    20          -0.13    7270       2812 258.6              121.3
 r1-dB b2-min c2 e2-neg n1      5Px4    22          -0.13    1405       2234  62.9              121.3
 r1-dB b2-min c2 e2-neg n1     10Px4     4           0.16   11818       7795 151.6               87.0
 r1-dB b2-min c2 e2-neg n1     10Px4    11           0.16    7544       9107  82.8               87.0
 r1-dB b2-min c2 e2-neg n1     10Px4    18           0.16    6165      23107  26.7               87.0
 r1-dB b2-min c2 e2-neg n2      1Px4    23           0.16    1036        420 246.6              192.3
 r1-dB b2-min c2 e2-neg n2      1Px4    24           0.16     688        474 145.0              192.3
 r1-dB b2-min c2 e2-neg n2      1Px4    25           0.16     484        254 190.6              192.3
 r1-dB b2-min c2 e2-neg n2      5Px4     9          -0.13    1248       2945  42.4              124.2
 r1-dB b2-min c2 e2-neg n2      5Px4    20          -0.13    7270       2812 258.6              124.2
 r1-dB b2-min c2 e2-neg n2      5Px4    22          -0.13    1405       2234  62.9              124.2
 r1-dB b2-min c2 e2-neg n2     10Px4     4           0.16   11818       7795 151.6               63.8
 r1-dB b2-min c2 e2-neg n2     10Px4    11           0.16    7544       9107  82.8               63.8
 r1-dB b2-min c2 e2-neg n2     10Px4    18           0.16    6165      23107  26.7               63.8
 r1-dB b2-min c2 e2-pos n1      1Px4    23           0.16    1036        350 296.2              186.6
 r1-dB b2-min c2 e2-pos n1      1Px4    24           0.16     688        500 137.4              186.6
 r1-dB b2-min c2 e2-pos n1      1Px4    25           0.16     484        384 126.1              186.6
 r1-dB b2-min c2 e2-pos n1      5Px4     9           0.16    1397       5827  24.0               63.7
 r1-dB b2-min c2 e2-pos n1      5Px4    20           0.16    1928       9608  20.1               63.7
 r1-dB b2-min c2 e2-pos n1      5Px4    22           0.16    2477       1685 147.0               63.7
 r1-dB b2-min c2 e2-pos n1     10Px4     4           0.16   11818       8352 141.5              105.5
 r1-dB b2-min c2 e2-pos n1     10Px4    11           0.16    7544       6089 123.9              105.5
 r1-dB b2-min c2 e2-pos n1     10Px4    18           0.16    6165      12064  51.1              105.5

1Px4 per-dish percent over the partial combinations: {23: {'min': 170.2, 'median': 296.2, 'max': 380.1}, 24: {'min': 75.2, 'median': 137.4, 'max': 145.0}, 25: {'min': 126.1, 'median': 179.4, 'max': 279.5}}

1Px4 normalized center against a common offset subtracted from every dish (r2 c1 e1 n1, and the partial layouts with c2):
offset            0      25     50     75     100    125    150    175    200    225    250    275    300
r2 c1 e1 n1     124.0  125.2  126.5  127.9  129.5  131.3  133.3  135.6  138.3  141.4  145.1  149.5  154.8
r2 c2 e1 n1     139.5  141.5  143.7  146.2  149.0  152.1  155.7  159.8  164.6  170.3  177.0  185.1  195.2
r1-dA c2 e1 n2  173.9  177.7  182.0  186.8  192.1  198.2  205.1  213.1  222.3  233.3  246.3  262.3  282.0
r1-dB c2 e1 n1  167.7  171.3  175.3  179.8  184.9  190.7  197.3  205.0  214.1  224.9  238.1  256.4  281.2

Result: partially reproduced. Under the primary reading (both diagonals, raw counts, the two central ROIs, the mean of the two ends, each dish normalized) the center is 124.0% of the periphery for 1Px4, 86.7% for 5Px4 and 58.7% for 10Px4; per dish 107.2 to 151.6%, 73.2 to 106.2% and 34.5 to 77.4%. No combination of readings puts all three values within tolerance. 1Px4 is within 180 to 220% and above both others in 13 of the 108 combinations of the claims file's readings, which meets its rule for a partial reproduction: 5 with the dark offset b2-dark and 8 with b2-min, none with raw counts, and all on a single diagonal with the peak taken as the highest ROI between the minima (c2). These rows are fragile. Their center is one ROI 36 um wide, and across them 1Px4 per dish is 170.2 to 380.1% for dish 23, 75.2 to 145.0% for dish 24 and 126.1 to 279.5% for dish 25. The 1Px4 value on diagonal A with c2, e1 and n2 passes 180% once a common offset of 50 counts is subtracted, less than every dark offset estimated here, and stays within 220% up to 175 counts (222.3% at 200); this sweep covers 1Px4 only and does not check the order against 5Px4 and 10Px4. On both diagonals with c1 it stays below 155% for offsets up to 300 counts. 1Px4 is above both others in 96 of 108. 10Px4 is within 90 to 110% in 14 of 108, and is below its periphery under the primary reading. With the ROI extent from the footprints, 1 of 108 combinations is partial (raw counts) and none reproduced (primary 116.1, 105.1 and 68.0%). The variant b2-fp, outside the readings, gives 7 partial of 36. So the ranking holds, "almost twofold" for 1Px4 is reached only on a single diagonal with a dark offset subtracted, and the 40-packet center is mostly well below its periphery, not "about the same".

fig6-trough-half-of-center¶

For all packet numbers the central peak was clearly discernible, and the least affected area of the monolayer, between the center and the electrodes, showed about 2-fold lower intensity than the center.

Criterion (claims file): the least affected area is the minimum of the mean profile between the center and the most peripheral ROI on each side, (t1) the mean of the two sides or (t2) each side; reproduced when center over least affected area is within 1.8 to 2.2 for all three treatments; partially reproduced when within tolerance for one or two and above 1 for all.

In [16]:
def trough_table(extent):
    rows = []
    for lay in cancan.LAYOUTS:
        for bg in cancan.BACKGROUNDS + cancan.VARIANT_BACKGROUNDS:
            data = table(lay, bg, extent)
            for cen in cancan.CENTERS:
                for norm in cancan.NORMALIZATIONS:
                    for per in (cancan.PERIPHERIES if norm == "n1" else ("e1",)):
                        for tro in cancan.TROUGHS:
                            row = {"layout": lay, "bg": bg, "center": cen, "norm": norm, "periphery": per, "trough": tro}
                            for t in TREATMENTS:
                                pos, P = data[t]
                                Q = cancan.mean_profile(P, per, norm)
                                ci, mins = cancan.center_index(Q, cen), cancan.side_minima(Q)
                                row[t] = cancan.center_value(Q, ci)[0] / cancan.trough_value(Q, mins, tro)[0]
                            rows.append(row)
    ftr = pd.DataFrame(rows)
    inside = ftr[TREATMENTS].apply(lambda s: s.between(1.8, 2.2))
    ftr["n_within"] = inside.sum(axis=1)
    ftr["all_above_1"] = (ftr[TREATMENTS] > 1).all(axis=1)
    ftr["outcome"] = np.where(ftr.n_within == 3, "reproduced",
                              np.where((ftr.n_within >= 1) & ftr.all_above_1, "partially", "not"))
    keep = ftr.bg.isin(cancan.BACKGROUNDS)
    return ftr, inside, keep


allftr, allinside, keep = trough_table("camera")
ftr, inside = allftr[keep], allinside[keep]
print("Rows with n2 do not depend on the periphery reading and are shown once (e1).")
print(ftr.round(2).to_string(index=False))
print("\nReadings of the claims file (backgrounds", cancan.BACKGROUNDS, "): combinations by outcome", ftr.outcome.value_counts().to_dict(), "of", len(ftr))
for t in TREATMENTS:
    print(f"{t}: center / least affected {ftr[t].min():.2f} to {ftr[t].max():.2f}, median {ftr[t].median():.2f}; within 1.8-2.2 in {int(inside[t].sum())} of {len(ftr)}")
print("Above 1 for all three treatments in", int(ftr.all_above_1.sum()), "of", len(ftr))
print("By background, combinations by outcome:", allftr.groupby("bg").outcome.value_counts().unstack(fill_value=0).to_dict("index"))
print("10Px4 by background, range:", allftr.groupby("bg")["10Px4"].agg(["min", "max"]).round(2).to_dict("index"))
gtr, ginside, gkeep = trough_table("geometric")
print("\nVariant, ROI extent from the footprints (no pixel size): combinations by outcome", gtr[gkeep].outcome.value_counts().to_dict(), "of", int(gkeep.sum()))
for t in TREATMENTS:
    print(f"  {t}: {gtr[gkeep][t].min():.2f} to {gtr[gkeep][t].max():.2f}, median {gtr[gkeep][t].median():.2f}; within 1.8-2.2 in {int(ginside[gkeep][t].sum())}")
Rows with n2 do not depend on the periphery reading and are shown once (e1).
layout      bg center norm periphery trough  1Px4  5Px4  10Px4  n_within  all_above_1   outcome
    r2      b1     c1   n1        e1     t1  1.40  2.16   2.80         1         True partially
    r2      b1     c1   n1        e1 t2-neg  1.48  2.02   2.95         1         True partially
    r2      b1     c1   n1        e1 t2-pos  1.33  2.33   2.67         0         True       not
    r2      b1     c1   n1    e2-neg     t1  1.40  2.20   2.84         0         True       not
    r2      b1     c1   n1    e2-neg t2-neg  1.47  2.07   2.99         1         True partially
    r2      b1     c1   n1    e2-neg t2-pos  1.33  2.36   2.70         0         True       not
    r2      b1     c1   n1    e2-pos     t1  1.41  2.14   2.77         1         True partially
    r2      b1     c1   n1    e2-pos t2-neg  1.49  2.00   2.90         1         True partially
    r2      b1     c1   n1    e2-pos t2-pos  1.33  2.31   2.66         0         True       not
    r2      b1     c1   n2        e1     t1  1.41  2.29   2.75         0         True       not
    r2      b1     c1   n2        e1 t2-neg  1.48  2.15   2.84         1         True partially
    r2      b1     c1   n2        e1 t2-pos  1.34  2.45   2.67         0         True       not
    r2      b1     c2   n1        e1     t1  1.57  2.29   3.38         0         True       not
    r2      b1     c2   n1        e1 t2-neg  1.67  2.14   3.56         1         True partially
    r2      b1     c2   n1        e1 t2-pos  1.49  2.46   3.23         0         True       not
    r2      b1     c2   n1    e2-neg     t1  1.57  2.35   3.45         0         True       not
    r2      b1     c2   n1    e2-neg t2-neg  1.66  2.20   3.63         0         True       not
    r2      b1     c2   n1    e2-neg t2-pos  1.49  2.52   3.28         0         True       not
    r2      b1     c2   n1    e2-pos     t1  1.58  2.25   3.32         0         True       not
    r2      b1     c2   n1    e2-pos t2-neg  1.67  2.10   3.47         1         True partially
    r2      b1     c2   n1    e2-pos t2-pos  1.50  2.42   3.19         0         True       not
    r2      b1     c2   n2        e1     t1  1.56  2.43   3.27         0         True       not
    r2      b1     c2   n2        e1 t2-neg  1.64  2.28   3.38         0         True       not
    r2      b1     c2   n2        e1 t2-pos  1.49  2.60   3.17         0         True       not
    r2 b2-dark     c1   n1        e1     t1  1.48  2.25   2.89         0         True       not
    r2 b2-dark     c1   n1        e1 t2-neg  1.58  2.09   3.04         1         True partially
    r2 b2-dark     c1   n1        e1 t2-pos  1.39  2.43   2.75         0         True       not
    r2 b2-dark     c1   n1    e2-neg     t1  1.47  2.29   2.93         0         True       not
    r2 b2-dark     c1   n1    e2-neg t2-neg  1.57  2.14   3.09         1         True partially
    r2 b2-dark     c1   n1    e2-neg t2-pos  1.39  2.47   2.78         0         True       not
    r2 b2-dark     c1   n1    e2-pos     t1  1.48  2.23   2.86         0         True       not
    r2 b2-dark     c1   n1    e2-pos t2-neg  1.59  2.07   2.99         1         True partially
    r2 b2-dark     c1   n1    e2-pos t2-pos  1.39  2.41   2.73         0         True       not
    r2 b2-dark     c1   n2        e1     t1  1.49  2.38   2.83         0         True       not
    r2 b2-dark     c1   n2        e1 t2-neg  1.58  2.23   2.93         0         True       not
    r2 b2-dark     c1   n2        e1 t2-pos  1.40  2.56   2.74         0         True       not
    r2 b2-dark     c2   n1        e1     t1  1.69  2.38   3.50         0         True       not
    r2 b2-dark     c2   n1        e1 t2-neg  1.81  2.22   3.68         1         True partially
    r2 b2-dark     c2   n1        e1 t2-pos  1.58  2.58   3.33         0         True       not
    r2 b2-dark     c2   n1    e2-neg     t1  1.69  2.45   3.57         0         True       not
    r2 b2-dark     c2   n1    e2-neg t2-neg  1.80  2.29   3.76         0         True       not
    r2 b2-dark     c2   n1    e2-neg t2-pos  1.59  2.64   3.39         0         True       not
    r2 b2-dark     c2   n1    e2-pos     t1  1.69  2.34   3.43         0         True       not
    r2 b2-dark     c2   n1    e2-pos t2-neg  1.82  2.17   3.60         2         True partially
    r2 b2-dark     c2   n1    e2-pos t2-pos  1.59  2.54   3.28         0         True       not
    r2 b2-dark     c2   n2        e1     t1  1.67  2.53   3.38         0         True       not
    r2 b2-dark     c2   n2        e1 t2-neg  1.78  2.37   3.49         0         True       not
    r2 b2-dark     c2   n2        e1 t2-pos  1.58  2.73   3.27         0         True       not
    r2  b2-min     c1   n1        e1     t1  1.52  2.36   3.01         0         True       not
    r2  b2-min     c1   n1        e1 t2-neg  1.64  2.18   3.18         1         True partially
    r2  b2-min     c1   n1        e1 t2-pos  1.42  2.57   2.86         0         True       not
    r2  b2-min     c1   n1    e2-neg     t1  1.52  2.39   3.06         0         True       not
    r2  b2-min     c1   n1    e2-neg t2-neg  1.63  2.22   3.24         0         True       not
    r2  b2-min     c1   n1    e2-neg t2-pos  1.42  2.60   2.89         0         True       not
    r2  b2-min     c1   n1    e2-pos     t1  1.53  2.34   2.97         0         True       not
    r2  b2-min     c1   n1    e2-pos t2-neg  1.65  2.16   3.12         1         True partially
    r2  b2-min     c1   n1    e2-pos t2-pos  1.43  2.56   2.84         0         True       not
    r2  b2-min     c1   n2        e1     t1  1.53  2.49   2.94         0         True       not
    r2  b2-min     c1   n2        e1 t2-neg  1.64  2.31   3.05         0         True       not
    r2  b2-min     c1   n2        e1 t2-pos  1.44  2.70   2.84         0         True       not
    r2  b2-min     c2   n1        e1     t1  1.75  2.50   3.66         0         True       not
    r2  b2-min     c2   n1        e1 t2-neg  1.89  2.31   3.87         1         True partially
    r2  b2-min     c2   n1        e1 t2-pos  1.63  2.73   3.47         0         True       not
    r2  b2-min     c2   n1    e2-neg     t1  1.75  2.57   3.73         0         True       not
    r2  b2-min     c2   n1    e2-neg t2-neg  1.88  2.38   3.96         1         True partially
    r2  b2-min     c2   n1    e2-neg t2-pos  1.64  2.78   3.54         0         True       not
    r2  b2-min     c2   n1    e2-pos     t1  1.76  2.47   3.59         0         True       not
    r2  b2-min     c2   n1    e2-pos t2-neg  1.90  2.27   3.77         1         True partially
    r2  b2-min     c2   n1    e2-pos t2-pos  1.64  2.69   3.42         0         True       not
    r2  b2-min     c2   n2        e1     t1  1.73  2.65   3.52         0         True       not
    r2  b2-min     c2   n2        e1 t2-neg  1.85  2.46   3.65         1         True partially
    r2  b2-min     c2   n2        e1 t2-pos  1.63  2.87   3.40         0         True       not
 r1-dA      b1     c1   n1        e1     t1  1.66  2.86   4.46         0         True       not
 r1-dA      b1     c1   n1        e1 t2-neg  1.74  2.75   5.07         0         True       not
 r1-dA      b1     c1   n1        e1 t2-pos  1.58  2.97   3.99         0         True       not
 r1-dA      b1     c1   n1    e2-neg     t1  1.64  2.60   4.14         0         True       not
 r1-dA      b1     c1   n1    e2-neg t2-neg  1.72  2.52   4.68         0         True       not
 r1-dA      b1     c1   n1    e2-neg t2-pos  1.57  2.69   3.72         0         True       not
 r1-dA      b1     c1   n1    e2-pos     t1  1.67  3.16   4.59         0         True       not
 r1-dA      b1     c1   n1    e2-pos t2-neg  1.75  3.00   5.19         0         True       not
 r1-dA      b1     c1   n1    e2-pos t2-pos  1.60  3.34   4.11         0         True       not
 r1-dA      b1     c1   n2        e1     t1  1.64  3.07   4.11         0         True       not
 r1-dA      b1     c1   n2        e1 t2-neg  1.71  2.89   4.43         0         True       not
 r1-dA      b1     c1   n2        e1 t2-pos  1.58  3.28   3.83         0         True       not
 r1-dA      b1     c2   n1        e1     t1  2.68  3.15   7.24         0         True       not
 r1-dA      b1     c2   n1        e1 t2-neg  2.82  3.03   8.23         0         True       not
 r1-dA      b1     c2   n1        e1 t2-pos  2.56  3.28   6.47         0         True       not
 r1-dA      b1     c2   n1    e2-neg     t1  2.13  3.01   6.30         1         True partially
 r1-dA      b1     c2   n1    e2-neg t2-neg  2.24  2.91   7.12         0         True       not
 r1-dA      b1     c2   n1    e2-neg t2-pos  2.04  3.11   5.66         1         True partially
 r1-dA      b1     c2   n1    e2-pos     t1  2.88  3.34   7.56         0         True       not
 r1-dA      b1     c2   n1    e2-pos t2-neg  3.02  3.17   8.55         0         True       not
 r1-dA      b1     c2   n1    e2-pos t2-pos  2.75  3.52   6.77         0         True       not
 r1-dA      b1     c2   n2        e1     t1  2.10  3.47   6.02         1         True partially
 r1-dA      b1     c2   n2        e1 t2-neg  2.19  3.27   6.49         1         True partially
 r1-dA      b1     c2   n2        e1 t2-pos  2.02  3.71   5.61         1         True partially
 r1-dA b2-dark     c1   n1        e1     t1  1.81  3.03   4.72         1         True partially
 r1-dA b2-dark     c1   n1        e1 t2-neg  1.93  2.90   5.41         1         True partially
 r1-dA b2-dark     c1   n1        e1 t2-pos  1.71  3.16   4.18         0         True       not
 r1-dA b2-dark     c1   n1    e2-neg     t1  1.79  2.74   4.35         0         True       not
 r1-dA b2-dark     c1   n1    e2-neg t2-neg  1.90  2.64   4.96         1         True partially
 r1-dA b2-dark     c1   n1    e2-neg t2-pos  1.69  2.85   3.88         0         True       not
 r1-dA b2-dark     c1   n1    e2-pos     t1  1.84  3.38   4.86         1         True partially
 r1-dA b2-dark     c1   n1    e2-pos t2-neg  1.95  3.19   5.54         1         True partially
 r1-dA b2-dark     c1   n1    e2-pos t2-pos  1.74  3.59   4.33         0         True       not
 r1-dA b2-dark     c1   n2        e1     t1  1.79  3.26   4.33         0         True       not
 r1-dA b2-dark     c1   n2        e1 t2-neg  1.89  3.05   4.69         1         True partially
 r1-dA b2-dark     c1   n2        e1 t2-pos  1.70  3.50   4.01         0         True       not
 r1-dA b2-dark     c2   n1        e1     t1  3.12  3.35   7.71         0         True       not
 r1-dA b2-dark     c2   n1        e1 t2-neg  3.32  3.21   8.85         0         True       not
 r1-dA b2-dark     c2   n1        e1 t2-pos  2.95  3.49   6.83         0         True       not
 r1-dA b2-dark     c2   n1    e2-neg     t1  2.39  3.20   6.67         0         True       not
 r1-dA b2-dark     c2   n1    e2-neg t2-neg  2.54  3.09   7.60         0         True       not
 r1-dA b2-dark     c2   n1    e2-neg t2-pos  2.26  3.33   5.94         0         True       not
 r1-dA b2-dark     c2   n1    e2-pos     t1  3.42  3.56   8.07         0         True       not
 r1-dA b2-dark     c2   n1    e2-pos t2-neg  3.63  3.36   9.20         0         True       not
 r1-dA b2-dark     c2   n1    e2-pos t2-pos  3.24  3.78   7.18         0         True       not
 r1-dA b2-dark     c2   n2        e1     t1  2.36  3.69   6.36         0         True       not
 r1-dA b2-dark     c2   n2        e1 t2-neg  2.49  3.45   6.90         0         True       not
 r1-dA b2-dark     c2   n2        e1 t2-pos  2.24  3.97   5.90         0         True       not
 r1-dA  b2-min     c1   n1        e1     t1  1.91  3.26   5.07         1         True partially
 r1-dA  b2-min     c1   n1        e1 t2-neg  2.05  3.11   5.90         1         True partially
 r1-dA  b2-min     c1   n1        e1 t2-pos  1.80  3.42   4.44         0         True       not
 r1-dA  b2-min     c1   n1    e2-neg     t1  1.88  2.93   4.67         1         True partially
 r1-dA  b2-min     c1   n1    e2-neg t2-neg  2.01  2.81   5.40         1         True partially
 r1-dA  b2-min     c1   n1    e2-neg t2-pos  1.76  3.06   4.11         0         True       not
 r1-dA  b2-min     c1   n1    e2-pos     t1  1.95  3.67   5.24         1         True partially
 r1-dA  b2-min     c1   n1    e2-pos t2-neg  2.08  3.45   6.04         1         True partially
 r1-dA  b2-min     c1   n1    e2-pos t2-pos  1.83  3.92   4.63         1         True partially
 r1-dA  b2-min     c1   n2        e1     t1  1.88  3.48   4.64         1         True partially
 r1-dA  b2-min     c1   n2        e1 t2-neg  1.99  3.23   5.06         1         True partially
 r1-dA  b2-min     c1   n2        e1 t2-pos  1.78  3.76   4.27         0         True       not
 r1-dA  b2-min     c2   n1        e1     t1  3.40  3.62   8.35         0         True       not
 r1-dA  b2-min     c2   n1        e1 t2-neg  3.63  3.45   9.73         0         True       not
 r1-dA  b2-min     c2   n1        e1 t2-pos  3.19  3.79   7.32         0         True       not
 r1-dA  b2-min     c2   n1    e2-neg     t1  2.56  3.48   7.20         0         True       not
 r1-dA  b2-min     c2   n1    e2-neg t2-neg  2.73  3.33   8.32         0         True       not
 r1-dA  b2-min     c2   n1    e2-neg t2-pos  2.40  3.63   6.34         0         True       not
 r1-dA  b2-min     c2   n1    e2-pos     t1  3.78  3.85   8.77         0         True       not
 r1-dA  b2-min     c2   n1    e2-pos t2-neg  4.04  3.62  10.11         0         True       not
 r1-dA  b2-min     c2   n1    e2-pos t2-pos  3.55  4.12   7.74         0         True       not
 r1-dA  b2-min     c2   n2        e1     t1  2.51  3.96   6.86         0         True       not
 r1-dA  b2-min     c2   n2        e1 t2-neg  2.66  3.68   7.50         0         True       not
 r1-dA  b2-min     c2   n2        e1 t2-pos  2.37  4.28   6.32         0         True       not
 r1-dB      b1     c1   n1        e1     t1  1.45  2.05   3.71         1         True partially
 r1-dB      b1     c1   n1        e1 t2-neg  1.60  2.03   3.73         1         True partially
 r1-dB      b1     c1   n1        e1 t2-pos  1.33  2.07   3.70         1         True partially
 r1-dB      b1     c1   n1    e2-neg     t1  1.43  2.34   3.79         0         True       not
 r1-dB      b1     c1   n1    e2-neg t2-neg  1.57  2.50   3.72         0         True       not
 r1-dB      b1     c1   n1    e2-neg t2-pos  1.32  2.21   3.86         0         True       not
 r1-dB      b1     c1   n1    e2-pos     t1  1.48  1.91   3.60         1         True partially
 r1-dB      b1     c1   n1    e2-pos t2-neg  1.62  1.86   3.71         1         True partially
 r1-dB      b1     c1   n1    e2-pos t2-pos  1.36  1.95   3.50         1         True partially
 r1-dB      b1     c1   n2        e1     t1  1.47  2.41   3.49         0         True       not
 r1-dB      b1     c1   n2        e1 t2-neg  1.61  2.60   3.55         0         True       not
 r1-dB      b1     c1   n2        e1 t2-pos  1.35  2.25   3.43         0         True       not
 r1-dB      b1     c2   n1        e1     t1  1.85  2.77   6.16         1         True partially
 r1-dB      b1     c2   n1        e1 t2-neg  2.04  2.75   6.19         1         True partially
 r1-dB      b1     c2   n1        e1 t2-pos  1.70  2.79   6.14         0         True       not
 r1-dB      b1     c2   n1    e2-neg     t1  1.83  3.45   6.32         1         True partially
 r1-dB      b1     c2   n1    e2-neg t2-neg  2.00  3.67   6.21         1         True partially
 r1-dB      b1     c2   n1    e2-neg t2-pos  1.68  3.25   6.43         0         True       not
 r1-dB      b1     c2   n1    e2-pos     t1  1.90  2.59   5.94         1         True partially
 r1-dB      b1     c2   n1    e2-pos t2-neg  2.08  2.52   6.11         1         True partially
 r1-dB      b1     c2   n1    e2-pos t2-pos  1.74  2.65   5.77         0         True       not
 r1-dB      b1     c2   n2        e1     t1  1.87  3.53   5.54         1         True partially
 r1-dB      b1     c2   n2        e1 t2-neg  2.05  3.80   5.64         1         True partially
 r1-dB      b1     c2   n2        e1 t2-pos  1.72  3.29   5.45         0         True       not
 r1-dB b2-dark     c1   n1        e1     t1  1.54  2.13   3.87         1         True partially
 r1-dB b2-dark     c1   n1        e1 t2-neg  1.73  2.11   3.89         1         True partially
 r1-dB b2-dark     c1   n1        e1 t2-pos  1.39  2.15   3.86         1         True partially
 r1-dB b2-dark     c1   n1    e2-neg     t1  1.51  2.46   3.96         0         True       not
 r1-dB b2-dark     c1   n1    e2-neg t2-neg  1.69  2.63   3.88         0         True       not
 r1-dB b2-dark     c1   n1    e2-neg t2-pos  1.37  2.30   4.03         0         True       not
 r1-dB b2-dark     c1   n1    e2-pos     t1  1.58  1.98   3.76         1         True partially
 r1-dB b2-dark     c1   n1    e2-pos t2-neg  1.77  1.93   3.88         1         True partially
 r1-dB b2-dark     c1   n1    e2-pos t2-pos  1.43  2.03   3.65         1         True partially
 r1-dB b2-dark     c1   n2        e1     t1  1.57  2.53   3.63         0         True       not
 r1-dB b2-dark     c1   n2        e1 t2-neg  1.75  2.74   3.69         0         True       not
 r1-dB b2-dark     c1   n2        e1 t2-pos  1.42  2.35   3.56         0         True       not
 r1-dB b2-dark     c2   n1        e1     t1  2.03  2.91   6.47         1         True partially
 r1-dB b2-dark     c2   n1        e1 t2-neg  2.27  2.88   6.50         0         True       not
 r1-dB b2-dark     c2   n1        e1 t2-pos  1.83  2.94   6.45         1         True partially
 r1-dB b2-dark     c2   n1    e2-neg     t1  2.01  3.65   6.63         1         True partially
 r1-dB b2-dark     c2   n1    e2-neg t2-neg  2.24  3.91   6.51         0         True       not
 r1-dB b2-dark     c2   n1    e2-neg t2-pos  1.82  3.42   6.76         1         True partially
 r1-dB b2-dark     c2   n1    e2-pos     t1  2.09  2.73   6.23         1         True partially
 r1-dB b2-dark     c2   n1    e2-pos t2-neg  2.34  2.66   6.43         0         True       not
 r1-dB b2-dark     c2   n1    e2-pos t2-pos  1.88  2.81   6.05         1         True partially
 r1-dB b2-dark     c2   n2        e1     t1  2.05  3.74   5.80         1         True partially
 r1-dB b2-dark     c2   n2        e1 t2-neg  2.29  4.06   5.90         0         True       not
 r1-dB b2-dark     c2   n2        e1 t2-pos  1.86  3.47   5.70         1         True partially
 r1-dB  b2-min     c1   n1        e1     t1  1.60  2.24   4.11         0         True       not
 r1-dB  b2-min     c1   n1        e1 t2-neg  1.81  2.21   4.13         1         True partially
 r1-dB  b2-min     c1   n1        e1 t2-pos  1.43  2.27   4.09         0         True       not
 r1-dB  b2-min     c1   n1    e2-neg     t1  1.56  2.59   4.20         0         True       not
 r1-dB  b2-min     c1   n1    e2-neg t2-neg  1.76  2.78   4.11         0         True       not
 r1-dB  b2-min     c1   n1    e2-neg t2-pos  1.40  2.41   4.28         0         True       not
 r1-dB  b2-min     c1   n1    e2-pos     t1  1.64  2.08   3.99         1         True partially
 r1-dB  b2-min     c1   n1    e2-pos t2-neg  1.86  2.02   4.13         2         True partially
 r1-dB  b2-min     c1   n1    e2-pos t2-pos  1.47  2.14   3.86         1         True partially
 r1-dB  b2-min     c1   n2        e1     t1  1.62  2.67   3.82         0         True       not
 r1-dB  b2-min     c1   n2        e1 t2-neg  1.83  2.92   3.90         1         True partially
 r1-dB  b2-min     c1   n2        e1 t2-pos  1.46  2.47   3.75         0         True       not
 r1-dB  b2-min     c2   n1        e1     t1  2.13  3.08   6.93         1         True partially
 r1-dB  b2-min     c2   n1        e1 t2-neg  2.42  3.04   6.96         0         True       not
 r1-dB  b2-min     c2   n1        e1 t2-pos  1.90  3.13   6.89         1         True partially
 r1-dB  b2-min     c2   n1    e2-neg     t1  2.12  3.88   7.10         1         True partially
 r1-dB  b2-min     c2   n1    e2-neg t2-neg  2.39  4.17   6.95         0         True       not
 r1-dB  b2-min     c2   n1    e2-neg t2-pos  1.90  3.62   7.24         1         True partially
 r1-dB  b2-min     c2   n1    e2-pos     t1  2.20  2.95   6.67         0         True       not
 r1-dB  b2-min     c2   n1    e2-pos t2-neg  2.50  2.87   6.90         0         True       not
 r1-dB  b2-min     c2   n1    e2-pos t2-pos  1.97  3.04   6.45         1         True partially
 r1-dB  b2-min     c2   n2        e1     t1  2.16  4.00   6.16         1         True partially
 r1-dB  b2-min     c2   n2        e1 t2-neg  2.44  4.37   6.27         0         True       not
 r1-dB  b2-min     c2   n2        e1 t2-pos  1.94  3.68   6.04         1         True partially

Readings of the claims file (backgrounds ('b1', 'b2-dark', 'b2-min') ): combinations by outcome {'not': 137, 'partially': 79} of 216
1Px4: center / least affected 1.32 to 4.04, median 1.74; within 1.8-2.2 in 52 of 216
5Px4: center / least affected 1.86 to 4.37, median 2.69; within 1.8-2.2 in 29 of 216
10Px4: center / least affected 2.66 to 10.11, median 4.11; within 1.8-2.2 in 0 of 216
Above 1 for all three treatments in 216 of 216
By background, combinations by outcome: {'b1': {'not': 45, 'partially': 27}, 'b2-dark': {'not': 47, 'partially': 25}, 'b2-fp': {'not': 58, 'partially': 14}, 'b2-min': {'not': 45, 'partially': 27}}
10Px4 by background, range: {'b1': {'min': 2.66, 'max': 8.55}, 'b2-dark': {'min': 2.73, 'max': 9.2}, 'b2-fp': {'min': 3.05, 'max': 12.06}, 'b2-min': {'min': 2.84, 'max': 10.11}}

Variant, ROI extent from the footprints (no pixel size): combinations by outcome {'not': 147, 'partially': 69} of 216
  1Px4: 1.37 to 4.28, median 1.77; within 1.8-2.2 in 51
  5Px4: 2.08 to 4.31, median 2.69; within 1.8-2.2 in 18
  10Px4: 2.68 to 9.27, median 4.16; within 1.8-2.2 in 0
In [17]:
# Per dish, primary reading (r2, c1, t1) under each background, minima taken from the treatment's mean profile.
rows = []
for bg in cancan.BACKGROUNDS + cancan.VARIANT_BACKGROUNDS:
    data = table("r2", bg)
    for t in TREATMENTS:
        pos, P = data[t]
        m = P.mean(0)
        ci, mins = cancan.center_index(m, "c1"), cancan.side_minima(m)
        for d, p in zip(dishes[t], P):
            rows.append({"bg": bg, "treatment": t, "dish": d["dish"],
                         "center_over_trough_t1": round(float(cancan.center_value(p, ci)[0] / cancan.trough_value(p, mins, "t1")[0]), 2)})
print(pd.DataFrame(rows).pivot_table(index=["treatment", "dish"], columns="bg", values="center_over_trough_t1", sort=False).to_string())
bg                b1  b2-dark  b2-min  b2-fp
treatment dish                              
1Px4      23    1.43     1.49    1.54   1.91
          24    1.48     1.58    1.63   2.07
          25    1.29     1.36    1.40   1.72
5Px4      9     2.38     2.49    2.65   3.07
          20    2.50     2.58    2.64   2.90
          22    1.86     1.94    2.04   2.26
10Px4     4     2.92     3.00    3.10   3.28
          11    2.81     2.91    3.07   3.42
          18    2.52     2.59    2.67   2.87

Result: partially reproduced. Under the primary reading the center over the least affected area is 1.40 for 1Px4, 2.16 for 5Px4 and 2.80 for 10Px4. Over the 216 combinations of the claims file's readings it is 1.32 to 4.04 for 1Px4 (within 1.8 to 2.2 in 52), 1.86 to 4.37 for 5Px4 (in 29) and 2.66 to 10.11 for 10Px4 (in none). No combination puts all three within tolerance; 79 put one or two within it, with all three above 1 (27 with raw counts, 25 with b2-dark and 27 with b2-min). The central peak is discernible for every treatment (the ratio is above 1 in all 216 combinations), but the ratio grows with the number of packets, so "about 2-fold for all the treatments" does not hold at 40 packets; per dish, 10Px4 is 2.52 to 2.92 with raw counts. The ROI extent from the footprints gives 69 partial and none reproduced (10Px4 2.68 to 9.27); the variant b2-fp, outside the readings, gives 14 partial of 72 (10Px4 3.05 to 12.06).

fig6-peak-narrower¶

The central peak appeared narrower (sharper) with increasing number of packets.

Criterion (claims file): (w1) full width of the central peak at half its height above the least affected area (the mean of the two side minima); (w2) the distance between the two minima on either side of the center. Each on the mean normalized profile, also with LOESS fits of span 0.25 and 0.5. Reproduced when, under a reading, width decreases from 4 to 20 to 40 packets; partially reproduced when 40 is narrower than 4 but 20 is out of order. The center reading sets where the half-height walk starts and the peak height (c1: the two central ROIs; c2: the highest ROI between the minima).

In [18]:
rows = []
for lay in cancan.LAYOUTS:
    for bg in cancan.BACKGROUNDS + cancan.VARIANT_BACKGROUNDS:
        data = table(lay, bg)
        for norm in cancan.NORMALIZATIONS:
            for per in (cancan.PERIPHERIES if norm == "n1" else ("e1",)):
                for sm in cancan.SMOOTHING:
                    for cen in cancan.CENTERS:
                        row = {"layout": lay, "bg": bg, "norm": norm, "periphery": per, "smoothing": sm, "center": cen}
                        for t in TREATMENTS:
                            pos, P = data[t]
                            Q = cancan.mean_profile(P, per, norm)
                            if sm != "none":
                                Q = cancan.loess(pos, Q, float(sm.split("-")[1]))
                            ci, mins = cancan.center_index(Q, cen), cancan.side_minima(Q)
                            start = ci[0] if len(ci) == 1 else (ci[0] if Q[ci[0]] >= Q[ci[1]] else ci[1])
                            peak = cancan.center_value(Q, ci)[0]
                            base = cancan.trough_value(Q, mins, "t1")[0]
                            row[f"w1_{t}"] = cancan.half_max_width(pos, Q, start, peak, base)
                            row[f"w2_{t}"] = pos[mins[1]] - pos[mins[0]]
                        rows.append(row)
allfw = pd.DataFrame(rows)
for w in ["w1", "w2"]:
    a, b, c = (allfw[f"{w}_{t}"] for t in TREATMENTS)
    allfw[f"{w}_outcome"] = np.where((a > b) & (b > c), "reproduced", np.where(c < a, "partially", "not"))
fw = allfw[allfw.bg.isin(cancan.BACKGROUNDS)]
# w2 does not depend on the center reading: count it once per combination of the other readings.
fw2 = fw[fw.center == "c1"]
print("Widths in mm. Rows with n2 do not depend on the periphery reading and are shown once (e1);"
      " w2 does not depend on the center reading and is counted once.")
print(fw.round(3).to_string(index=False))
for w, f in [("w1", fw), ("w2", fw2)]:
    print(f"\n{w}: combinations by outcome", f[f"{w}_outcome"].value_counts().to_dict(), "of", len(f),
          "; with n1 only:", f[f.norm == "n1"][f"{w}_outcome"].value_counts().to_dict(),
          "; with n2 only:", f[f.norm == "n2"][f"{w}_outcome"].value_counts().to_dict())
    for t in TREATMENTS:
        s = f[f"{w}_{t}"]
        print(f"  {t}: {s.min():.3f} to {s.max():.3f} mm, median {s.median():.3f}; not measurable (NaN) in {int(s.isna().sum())}")
    print("  by smoothing:", f.groupby("smoothing")[f"{w}_outcome"].value_counts().unstack(fill_value=0).to_dict("index"))
    print("  by background:", f.groupby("bg")[f"{w}_outcome"].value_counts().unstack(fill_value=0).to_dict("index"))
print("\nw1 by center reading:", fw.groupby("center")["w1_outcome"].value_counts().unstack(fill_value=0).to_dict("index"))
v = allfw[allfw.bg.isin(cancan.VARIANT_BACKGROUNDS)]
print("Variant b2-fp (not a reading of the claims file): w1", v.w1_outcome.value_counts().to_dict(), "of", len(v),
      "; w2", v[v.center == "c1"].w2_outcome.value_counts().to_dict(), "of", int((v.center == "c1").sum()))
Widths in mm. Rows with n2 do not depend on the periphery reading and are shown once (e1); w2 does not depend on the center reading and is counted once.
layout      bg norm periphery  smoothing center  w1_1Px4  w2_1Px4  w1_5Px4  w2_5Px4  w1_10Px4  w2_10Px4 w1_outcome w2_outcome
    r2      b1   n1        e1       none     c1    1.051    1.363    0.545    1.722     0.488     1.148 reproduced  partially
    r2      b1   n1        e1       none     c2    0.591    1.363    0.511    1.722     0.437     1.148 reproduced  partially
    r2      b1   n1        e1 loess-0.25     c1    0.800    1.722    0.654    1.793     0.560     1.363 reproduced  partially
    r2      b1   n1        e1 loess-0.25     c2    0.791    1.722    0.649    1.793     0.546     1.363 reproduced  partially
    r2      b1   n1        e1  loess-0.5     c1    0.992    2.367    0.863    1.650     0.817     1.435 reproduced reproduced
    r2      b1   n1        e1  loess-0.5     c2    0.980    2.367    0.860    1.650     0.813     1.435 reproduced reproduced
    r2      b1   n1    e2-neg       none     c1    1.053    1.363    0.563    1.722     0.486     1.148 reproduced  partially
    r2      b1   n1    e2-neg       none     c2    0.601    1.363    0.518    1.722     0.432     1.148 reproduced  partially
    r2      b1   n1    e2-neg loess-0.25     c1    0.802    1.722    0.648    1.793     0.557     1.363 reproduced  partially
    r2      b1   n1    e2-neg loess-0.25     c2    0.793    1.722    0.640    1.793     0.543     1.363 reproduced  partially
    r2      b1   n1    e2-neg  loess-0.5     c1    0.980    2.367    0.861    1.578     0.822     1.435 reproduced reproduced
    r2      b1   n1    e2-neg  loess-0.5     c2    0.969    2.367    0.856    1.578     0.818     1.435 reproduced reproduced
    r2      b1   n1    e2-pos       none     c1    1.051    1.507    0.535    1.722     0.492     1.076 reproduced  partially
    r2      b1   n1    e2-pos       none     c2    0.584    1.507    0.506    1.722     0.442     1.076 reproduced  partially
    r2      b1   n1    e2-pos loess-0.25     c1    0.802    2.439    0.657    1.793     0.563     1.363 reproduced reproduced
    r2      b1   n1    e2-pos loess-0.25     c2    0.793    2.439    0.654    1.793     0.549     1.363 reproduced reproduced
    r2      b1   n1    e2-pos  loess-0.5     c1    1.005    2.367    0.865    1.578     0.811     1.435 reproduced reproduced
    r2      b1   n1    e2-pos  loess-0.5     c2    0.991    2.367    0.863    1.578     0.808     1.435 reproduced reproduced
    r2      b1   n2        e1       none     c1    0.970    1.363    0.552    1.722     0.499     1.076 reproduced  partially
    r2      b1   n2        e1       none     c2    0.598    1.363    0.510    1.722     0.452     1.076 reproduced  partially
    r2      b1   n2        e1 loess-0.25     c1    0.805    1.793    0.619    1.722     0.570     1.363 reproduced reproduced
    r2      b1   n2        e1 loess-0.25     c2    0.796    1.793    0.608    1.722     0.556     1.363 reproduced reproduced
    r2      b1   n2        e1  loess-0.5     c1    0.978    2.367    0.858    1.578     0.806     1.435 reproduced reproduced
    r2      b1   n2        e1  loess-0.5     c2    0.967    2.367    0.853    1.578     0.803     1.435 reproduced reproduced
    r2 b2-dark   n1        e1       none     c1    1.052    1.363    0.545    1.722     0.488     1.148 reproduced  partially
    r2 b2-dark   n1        e1       none     c2    0.592    1.363    0.511    1.722     0.437     1.148 reproduced  partially
    r2 b2-dark   n1        e1 loess-0.25     c1    0.800    1.722    0.656    1.793     0.560     1.363 reproduced  partially
    r2 b2-dark   n1        e1 loess-0.25     c2    0.791    1.722    0.650    1.793     0.546     1.363 reproduced  partially
    r2 b2-dark   n1        e1  loess-0.5     c1    0.991    2.367    0.863    1.650     0.817     1.435 reproduced reproduced
    r2 b2-dark   n1        e1  loess-0.5     c2    0.980    2.367    0.860    1.650     0.813     1.435 reproduced reproduced
    r2 b2-dark   n1    e2-neg       none     c1    1.054    1.363    0.563    1.722     0.486     1.148 reproduced  partially
    r2 b2-dark   n1    e2-neg       none     c2    0.603    1.363    0.519    1.722     0.432     1.148 reproduced  partially
    r2 b2-dark   n1    e2-neg loess-0.25     c1    0.802    1.722    0.650    1.793     0.557     1.363 reproduced  partially
    r2 b2-dark   n1    e2-neg loess-0.25     c2    0.794    1.722    0.641    1.793     0.543     1.363 reproduced  partially
    r2 b2-dark   n1    e2-neg  loess-0.5     c1    0.978    2.367    0.861    1.578     0.822     1.435 reproduced reproduced
    r2 b2-dark   n1    e2-neg  loess-0.5     c2    0.966    2.367    0.856    1.578     0.818     1.435 reproduced reproduced
    r2 b2-dark   n1    e2-pos       none     c1    1.052    1.507    0.536    1.722     0.492     1.076 reproduced  partially
    r2 b2-dark   n1    e2-pos       none     c2    0.583    1.507    0.507    1.722     0.442     1.076 reproduced  partially
    r2 b2-dark   n1    e2-pos loess-0.25     c1    0.802    2.439    0.659    1.793     0.563     1.363 reproduced reproduced
    r2 b2-dark   n1    e2-pos loess-0.25     c2    0.793    2.439    0.655    1.793     0.549     1.363 reproduced reproduced
    r2 b2-dark   n1    e2-pos  loess-0.5     c1    1.006    2.367    0.865    1.578     0.811     1.435 reproduced reproduced
    r2 b2-dark   n1    e2-pos  loess-0.5     c2    0.993    2.367    0.863    1.578     0.808     1.435 reproduced reproduced
    r2 b2-dark   n2        e1       none     c1    0.970    1.363    0.552    1.722     0.499     1.076 reproduced  partially
    r2 b2-dark   n2        e1       none     c2    0.598    1.363    0.510    1.722     0.452     1.076 reproduced  partially
    r2 b2-dark   n2        e1 loess-0.25     c1    0.805    1.793    0.619    1.722     0.570     1.363 reproduced reproduced
    r2 b2-dark   n2        e1 loess-0.25     c2    0.796    1.793    0.608    1.722     0.556     1.363 reproduced reproduced
    r2 b2-dark   n2        e1  loess-0.5     c1    0.978    2.367    0.858    1.578     0.806     1.435 reproduced reproduced
    r2 b2-dark   n2        e1  loess-0.5     c2    0.967    2.367    0.853    1.578     0.803     1.435 reproduced reproduced
    r2  b2-min   n1        e1       none     c1    1.052    1.363    0.545    1.722     0.488     1.148 reproduced  partially
    r2  b2-min   n1        e1       none     c2    0.591    1.363    0.511    1.722     0.437     1.148 reproduced  partially
    r2  b2-min   n1        e1 loess-0.25     c1    0.800    2.439    0.658    1.793     0.560     1.363 reproduced reproduced
    r2  b2-min   n1        e1 loess-0.25     c2    0.791    2.439    0.652    1.793     0.545     1.363 reproduced reproduced
    r2  b2-min   n1        e1  loess-0.5     c1    0.994    2.367    0.864    1.650     0.817     1.435 reproduced reproduced
    r2  b2-min   n1        e1  loess-0.5     c2    0.982    2.367    0.861    1.650     0.813     1.435 reproduced reproduced
    r2  b2-min   n1    e2-neg       none     c1    1.055    1.363    0.563    1.722     0.485     1.148 reproduced  partially
    r2  b2-min   n1    e2-neg       none     c2    0.603    1.363    0.519    1.722     0.432     1.148 reproduced  partially
    r2  b2-min   n1    e2-neg loess-0.25     c1    0.802    1.722    0.651    1.793     0.557     1.363 reproduced  partially
    r2  b2-min   n1    e2-neg loess-0.25     c2    0.793    1.722    0.643    1.793     0.543     1.363 reproduced  partially
    r2  b2-min   n1    e2-neg  loess-0.5     c1    0.978    2.367    0.861    1.578     0.822     1.435 reproduced reproduced
    r2  b2-min   n1    e2-neg  loess-0.5     c2    0.967    2.367    0.857    1.578     0.818     1.435 reproduced reproduced
    r2  b2-min   n1    e2-pos       none     c1    1.052    1.507    0.536    1.722     0.491     1.076 reproduced  partially
    r2  b2-min   n1    e2-pos       none     c2    0.582    1.507    0.507    1.722     0.442     1.076 reproduced  partially
    r2  b2-min   n1    e2-pos loess-0.25     c1    0.803    2.439    0.661    1.793     0.563     1.363 reproduced reproduced
    r2  b2-min   n1    e2-pos loess-0.25     c2    0.794    2.439    0.658    1.793     0.549     1.363 reproduced reproduced
    r2  b2-min   n1    e2-pos  loess-0.5     c1    1.010    2.367    0.866    1.578     0.812     1.435 reproduced reproduced
    r2  b2-min   n1    e2-pos  loess-0.5     c2    0.996    2.367    0.864    1.578     0.808     1.435 reproduced reproduced
    r2  b2-min   n2        e1       none     c1    0.970    1.363    0.552    1.722     0.499     1.076 reproduced  partially
    r2  b2-min   n2        e1       none     c2    0.598    1.363    0.510    1.722     0.452     1.076 reproduced  partially
    r2  b2-min   n2        e1 loess-0.25     c1    0.805    1.793    0.619    1.722     0.570     1.363 reproduced reproduced
    r2  b2-min   n2        e1 loess-0.25     c2    0.796    1.793    0.608    1.722     0.556     1.363 reproduced reproduced
    r2  b2-min   n2        e1  loess-0.5     c1    0.978    2.367    0.858    1.578     0.806     1.435 reproduced reproduced
    r2  b2-min   n2        e1  loess-0.5     c2    0.967    2.367    0.853    1.578     0.803     1.435 reproduced reproduced
 r1-dA      b1   n1        e1       none     c1    0.776    2.116    0.405    2.260     0.345     1.829 reproduced  partially
 r1-dA      b1   n1        e1       none     c2    0.078    2.116    0.082    2.260     0.118     1.829        not  partially
 r1-dA      b1   n1        e1 loess-0.25     c1    0.818    2.367    0.769    1.865     0.654     1.758 reproduced reproduced
 r1-dA      b1   n1        e1 loess-0.25     c2    0.818    2.367    0.739    1.865     0.619     1.758 reproduced reproduced
 r1-dA      b1   n1        e1  loess-0.5     c1    0.950    2.654    0.919    1.650     0.892     1.614 reproduced reproduced
 r1-dA      b1   n1        e1  loess-0.5     c2    0.942    2.654    0.918    1.650     0.888     1.614 reproduced reproduced
 r1-dA      b1   n1    e2-neg       none     c1    0.675    1.507    0.416    1.973     0.332     1.829 reproduced        not
 r1-dA      b1   n1    e2-neg       none     c2    0.183    1.507    0.192    1.973     0.121     1.829  partially        not
 r1-dA      b1   n1    e2-neg loess-0.25     c1    0.812    2.367    0.819    1.937     0.594     1.650  partially reproduced
 r1-dA      b1   n1    e2-neg loess-0.25     c2    0.811    2.367    0.791    1.937     0.576     1.650 reproduced reproduced
 r1-dA      b1   n1    e2-neg  loess-0.5     c1    0.919    2.762    0.913    1.650     0.876     1.542 reproduced reproduced
 r1-dA      b1   n1    e2-neg  loess-0.5     c2    0.914    2.762    0.912    1.650     0.873     1.542 reproduced reproduced
 r1-dA      b1   n1    e2-pos       none     c1    0.775    2.116    0.242    2.260     0.347     2.116  partially        not
 r1-dA      b1   n1    e2-pos       none     c2    0.078    2.116    0.072    2.260     0.117     2.116        not        not
 r1-dA      b1   n1    e2-pos loess-0.25     c1    0.822    2.367    0.716    1.829     0.669     1.793 reproduced reproduced
 r1-dA      b1   n1    e2-pos loess-0.25     c2    0.822    2.367    0.687    1.829     0.630     1.793 reproduced reproduced
 r1-dA      b1   n1    e2-pos  loess-0.5     c1    0.989    2.439    0.927    1.650     0.897     1.686 reproduced  partially
 r1-dA      b1   n1    e2-pos  loess-0.5     c2    0.978    2.439    0.925    1.650     0.894     1.686 reproduced  partially
 r1-dA      b1   n2        e1       none     c1    0.675    1.507    0.336    2.260     0.341     1.829  partially        not
 r1-dA      b1   n2        e1       none     c2    0.187    1.507    0.096    2.260     0.150     1.829  partially        not
 r1-dA      b1   n2        e1 loess-0.25     c1    0.806    2.367    0.712    1.793     0.597     1.471 reproduced reproduced
 r1-dA      b1   n2        e1 loess-0.25     c2    0.805    2.367    0.698    1.793     0.563     1.471 reproduced reproduced
 r1-dA      b1   n2        e1  loess-0.5     c1    0.893    2.762    0.921    1.686     0.857     1.507  partially reproduced
 r1-dA      b1   n2        e1  loess-0.5     c2    0.889    2.762    0.918    1.686     0.852     1.507  partially reproduced
 r1-dA b2-dark   n1        e1       none     c1    0.776    2.116    0.405    2.260     0.345     1.829 reproduced  partially
 r1-dA b2-dark   n1        e1       none     c2    0.078    2.116    0.082    2.260     0.118     1.829        not  partially
 r1-dA b2-dark   n1        e1 loess-0.25     c1    0.819    2.367    0.772    1.865     0.655     1.758 reproduced reproduced
 r1-dA b2-dark   n1        e1 loess-0.25     c2    0.818    2.367    0.741    1.865     0.620     1.758 reproduced reproduced
 r1-dA b2-dark   n1        e1  loess-0.5     c1    0.954    2.654    0.919    1.650     0.892     1.614 reproduced reproduced
 r1-dA b2-dark   n1        e1  loess-0.5     c2    0.945    2.654    0.917    1.650     0.888     1.614 reproduced reproduced
 r1-dA b2-dark   n1    e2-neg       none     c1    0.675    1.507    0.417    1.973     0.331     1.829 reproduced        not
 r1-dA b2-dark   n1    e2-neg       none     c2    0.184    1.507    0.192    1.973     0.120     1.829  partially        not
 r1-dA b2-dark   n1    e2-neg loess-0.25     c1    0.811    2.367    0.823    1.937     0.594     1.650  partially reproduced
 r1-dA b2-dark   n1    e2-neg loess-0.25     c2    0.811    2.367    0.795    1.937     0.576     1.650 reproduced reproduced
 r1-dA b2-dark   n1    e2-neg  loess-0.5     c1    0.917    2.762    0.913    1.650     0.876     1.542 reproduced reproduced
 r1-dA b2-dark   n1    e2-neg  loess-0.5     c2    0.912    2.762    0.912    1.650     0.873     1.542  partially reproduced
 r1-dA b2-dark   n1    e2-pos       none     c1    0.775    2.116    0.241    2.260     0.347     2.116  partially        not
 r1-dA b2-dark   n1    e2-pos       none     c2    0.078    2.116    0.072    2.260     0.117     2.116        not        not
 r1-dA b2-dark   n1    e2-pos loess-0.25     c1    0.823    2.367    0.716    1.829     0.670     1.829 reproduced  partially
 r1-dA b2-dark   n1    e2-pos loess-0.25     c2    0.822    2.367    0.686    1.829     0.631     1.829 reproduced  partially
 r1-dA b2-dark   n1    e2-pos  loess-0.5     c1    1.004    2.439    0.928    1.650     0.898     1.686 reproduced  partially
 r1-dA b2-dark   n1    e2-pos  loess-0.5     c2    0.992    2.439    0.925    1.650     0.894     1.686 reproduced  partially
 r1-dA b2-dark   n2        e1       none     c1    0.675    1.507    0.336    2.260     0.341     1.829  partially        not
 r1-dA b2-dark   n2        e1       none     c2    0.187    1.507    0.096    2.260     0.150     1.829  partially        not
 r1-dA b2-dark   n2        e1 loess-0.25     c1    0.806    2.367    0.712    1.793     0.597     1.471 reproduced reproduced
 r1-dA b2-dark   n2        e1 loess-0.25     c2    0.805    2.367    0.698    1.793     0.563     1.471 reproduced reproduced
 r1-dA b2-dark   n2        e1  loess-0.5     c1    0.893    2.762    0.921    1.686     0.857     1.507  partially reproduced
 r1-dA b2-dark   n2        e1  loess-0.5     c2    0.889    2.762    0.918    1.686     0.852     1.507  partially reproduced
 r1-dA  b2-min   n1        e1       none     c1    0.775    2.116    0.405    2.260     0.345     1.829 reproduced  partially
 r1-dA  b2-min   n1        e1       none     c2    0.078    2.116    0.083    2.260     0.118     1.829        not  partially
 r1-dA  b2-min   n1        e1 loess-0.25     c1    0.820    2.367    0.775    1.865     0.656     1.758 reproduced reproduced
 r1-dA  b2-min   n1        e1 loess-0.25     c2    0.820    2.367    0.744    1.865     0.621     1.758 reproduced reproduced
 r1-dA  b2-min   n1        e1  loess-0.5     c1    0.965    2.654    0.919    1.650     0.893     1.614 reproduced reproduced
 r1-dA  b2-min   n1        e1  loess-0.5     c2    0.956    2.654    0.917    1.650     0.889     1.614 reproduced reproduced
 r1-dA  b2-min   n1    e2-neg       none     c1    0.675    1.507    0.418    1.973     0.331     1.829 reproduced        not
 r1-dA  b2-min   n1    e2-neg       none     c2    0.183    1.507    0.191    1.973     0.120     1.829  partially        not
 r1-dA  b2-min   n1    e2-neg loess-0.25     c1    0.813    2.367    0.829    1.937     0.594     1.650  partially reproduced
 r1-dA  b2-min   n1    e2-neg loess-0.25     c2    0.812    2.367    0.800    1.937     0.576     1.650 reproduced reproduced
 r1-dA  b2-min   n1    e2-neg  loess-0.5     c1    0.923    2.726    0.912    1.650     0.876     1.542 reproduced reproduced
 r1-dA  b2-min   n1    e2-neg  loess-0.5     c2    0.918    2.726    0.912    1.650     0.874     1.542 reproduced reproduced
 r1-dA  b2-min   n1    e2-pos       none     c1    0.775    2.116    0.240    2.260     0.347     2.116  partially        not
 r1-dA  b2-min   n1    e2-pos       none     c2    0.078    2.116    0.072    2.260     0.117     2.116        not        not
 r1-dA  b2-min   n1    e2-pos loess-0.25     c1    0.824    2.367    0.715    1.829     0.671     1.829 reproduced  partially
 r1-dA  b2-min   n1    e2-pos loess-0.25     c2    0.824    2.367    0.685    1.829     0.632     1.829 reproduced  partially
 r1-dA  b2-min   n1    e2-pos  loess-0.5     c1    1.024    2.439    0.929    1.650     0.899     1.686 reproduced  partially
 r1-dA  b2-min   n1    e2-pos  loess-0.5     c2    1.011    2.439    0.926    1.650     0.895     1.686 reproduced  partially
 r1-dA  b2-min   n2        e1       none     c1    0.675    1.507    0.336    2.260     0.341     1.829  partially        not
 r1-dA  b2-min   n2        e1       none     c2    0.187    1.507    0.096    2.260     0.150     1.829  partially        not
 r1-dA  b2-min   n2        e1 loess-0.25     c1    0.806    2.367    0.712    1.793     0.597     1.471 reproduced reproduced
 r1-dA  b2-min   n2        e1 loess-0.25     c2    0.805    2.367    0.698    1.793     0.563     1.471 reproduced reproduced
 r1-dA  b2-min   n2        e1  loess-0.5     c1    0.893    2.762    0.921    1.686     0.857     1.507  partially reproduced
 r1-dA  b2-min   n2        e1  loess-0.5     c2    0.889    2.762    0.918    1.686     0.852     1.507  partially reproduced
 r1-dB      b1   n1        e1       none     c1    0.888    1.829    0.748    1.829     0.458     1.327 reproduced reproduced
 r1-dB      b1   n1        e1       none     c2    0.216    1.829    0.318    1.829     0.110     1.327  partially reproduced
 r1-dB      b1   n1        e1 loess-0.25     c1    0.940    1.829    0.670    1.650     0.569     1.291 reproduced reproduced
 r1-dB      b1   n1        e1 loess-0.25     c2    0.912    1.829    0.655    1.650     0.561     1.291 reproduced reproduced
 r1-dB      b1   n1        e1  loess-0.5     c1    1.125    2.439    0.813    1.507     0.801     1.435 reproduced reproduced
 r1-dB      b1   n1        e1  loess-0.5     c2    1.098    2.439    0.809    1.507     0.800     1.435 reproduced reproduced
 r1-dB      b1   n1    e2-neg       none     c1    0.890    1.865    0.676    1.829     0.460     1.327 reproduced reproduced
 r1-dB      b1   n1    e2-neg       none     c2    0.219    1.865    0.199    1.829     0.112     1.327 reproduced reproduced
 r1-dB      b1   n1    e2-neg loess-0.25     c1    0.953    1.865    0.627    1.614     0.570     1.291 reproduced reproduced
 r1-dB      b1   n1    e2-neg loess-0.25     c2    0.923    1.865    0.598    1.614     0.563     1.291 reproduced reproduced
 r1-dB      b1   n1    e2-neg  loess-0.5     c1    1.110    2.439    0.821    1.471     0.806     1.435 reproduced reproduced
 r1-dB      b1   n1    e2-neg  loess-0.5     c2    1.080    2.439    0.812    1.471     0.805     1.435 reproduced reproduced
 r1-dB      b1   n1    e2-pos       none     c1    0.886    1.829    0.858    1.937     0.455     1.327 reproduced  partially
 r1-dB      b1   n1    e2-pos       none     c2    0.212    1.829    0.316    1.937     0.106     1.327  partially  partially
 r1-dB      b1   n1    e2-pos loess-0.25     c1    0.940    2.583    0.708    1.650     0.568     1.291 reproduced reproduced
 r1-dB      b1   n1    e2-pos loess-0.25     c2    0.912    2.583    0.702    1.650     0.560     1.291 reproduced reproduced
 r1-dB      b1   n1    e2-pos  loess-0.5     c1    1.141    2.439    0.809    1.507     0.794     1.399 reproduced reproduced
 r1-dB      b1   n1    e2-pos  loess-0.5     c2    1.116    2.439    0.807    1.507     0.794     1.399 reproduced reproduced
 r1-dB      b1   n2        e1       none     c1    0.885    1.829    0.596    1.829     0.454     1.327 reproduced reproduced
 r1-dB      b1   n2        e1       none     c2    0.216    1.829    0.199    1.829     0.108     1.327 reproduced reproduced
 r1-dB      b1   n2        e1 loess-0.25     c1    0.928    1.829    0.617    1.578     0.569     1.291 reproduced reproduced
 r1-dB      b1   n2        e1 loess-0.25     c2    0.899    1.829    0.587    1.578     0.564     1.291 reproduced reproduced
 r1-dB      b1   n2        e1  loess-0.5     c1    1.117    2.439    0.823    1.471     0.785     1.399 reproduced reproduced
 r1-dB      b1   n2        e1  loess-0.5     c2    1.087    2.439    0.813    1.471     0.784     1.399 reproduced reproduced
 r1-dB b2-dark   n1        e1       none     c1    0.888    1.865    0.753    1.829     0.458     1.327 reproduced reproduced
 r1-dB b2-dark   n1        e1       none     c2    0.216    1.865    0.319    1.829     0.110     1.327  partially reproduced
 r1-dB b2-dark   n1        e1 loess-0.25     c1    0.942    1.829    0.671    1.650     0.569     1.291 reproduced reproduced
 r1-dB b2-dark   n1        e1 loess-0.25     c2    0.914    1.829    0.657    1.650     0.561     1.291 reproduced reproduced
 r1-dB b2-dark   n1        e1  loess-0.5     c1    1.124    2.439    0.813    1.507     0.801     1.435 reproduced reproduced
 r1-dB b2-dark   n1        e1  loess-0.5     c2    1.096    2.439    0.809    1.507     0.801     1.435 reproduced reproduced
 r1-dB b2-dark   n1    e2-neg       none     c1    0.891    1.865    0.676    1.829     0.292     1.327 reproduced reproduced
 r1-dB b2-dark   n1    e2-neg       none     c2    0.217    1.865    0.199    1.829     0.112     1.327 reproduced reproduced
 r1-dB b2-dark   n1    e2-neg loess-0.25     c1    0.958    1.865    0.627    1.614     0.570     1.291 reproduced reproduced
 r1-dB b2-dark   n1    e2-neg loess-0.25     c2    0.928    1.865    0.598    1.614     0.563     1.291 reproduced reproduced
 r1-dB b2-dark   n1    e2-neg  loess-0.5     c1    1.106    2.439    0.821    1.471     0.806     1.435 reproduced reproduced
 r1-dB b2-dark   n1    e2-neg  loess-0.5     c2    1.076    2.439    0.812    1.471     0.805     1.435 reproduced reproduced
 r1-dB b2-dark   n1    e2-pos       none     c1    0.886    1.829    0.859    1.937     0.455     1.327 reproduced  partially
 r1-dB b2-dark   n1    e2-pos       none     c2    0.212    1.829    0.241    1.937     0.106     1.327  partially  partially
 r1-dB b2-dark   n1    e2-pos loess-0.25     c1    0.941    2.583    0.710    1.650     0.568     1.291 reproduced reproduced
 r1-dB b2-dark   n1    e2-pos loess-0.25     c2    0.914    2.583    0.705    1.650     0.560     1.291 reproduced reproduced
 r1-dB b2-dark   n1    e2-pos  loess-0.5     c1    1.143    2.439    0.809    1.507     0.794     1.399 reproduced reproduced
 r1-dB b2-dark   n1    e2-pos  loess-0.5     c2    1.118    2.439    0.807    1.507     0.794     1.399 reproduced reproduced
 r1-dB b2-dark   n2        e1       none     c1    0.885    1.829    0.596    1.829     0.454     1.327 reproduced reproduced
 r1-dB b2-dark   n2        e1       none     c2    0.216    1.829    0.199    1.829     0.108     1.327 reproduced reproduced
 r1-dB b2-dark   n2        e1 loess-0.25     c1    0.928    1.829    0.617    1.578     0.569     1.291 reproduced reproduced
 r1-dB b2-dark   n2        e1 loess-0.25     c2    0.899    1.829    0.587    1.578     0.564     1.291 reproduced reproduced
 r1-dB b2-dark   n2        e1  loess-0.5     c1    1.117    2.439    0.823    1.471     0.785     1.399 reproduced reproduced
 r1-dB b2-dark   n2        e1  loess-0.5     c2    1.087    2.439    0.813    1.471     0.784     1.399 reproduced reproduced
 r1-dB  b2-min   n1        e1       none     c1    0.888    1.865    0.758    1.829     0.458     1.327 reproduced reproduced
 r1-dB  b2-min   n1        e1       none     c2    0.216    1.865    0.321    1.829     0.110     1.327  partially reproduced
 r1-dB  b2-min   n1        e1 loess-0.25     c1    0.944    1.829    0.674    1.650     0.569     1.291 reproduced reproduced
 r1-dB  b2-min   n1        e1 loess-0.25     c2    0.915    1.829    0.660    1.650     0.561     1.291 reproduced reproduced
 r1-dB  b2-min   n1        e1  loess-0.5     c1    1.126    2.439    0.813    1.507     0.801     1.435 reproduced reproduced
 r1-dB  b2-min   n1        e1  loess-0.5     c2    1.098    2.439    0.808    1.507     0.801     1.435 reproduced reproduced
 r1-dB  b2-min   n1    e2-neg       none     c1    0.892    1.865    0.677    1.829     0.292     1.327 reproduced reproduced
 r1-dB  b2-min   n1    e2-neg       none     c2    0.216    1.865    0.200    1.829     0.112     1.327 reproduced reproduced
 r1-dB  b2-min   n1    e2-neg loess-0.25     c1    0.962    1.865    0.628    1.614     0.570     1.291 reproduced reproduced
 r1-dB  b2-min   n1    e2-neg loess-0.25     c2    0.931    1.865    0.599    1.614     0.563     1.291 reproduced reproduced
 r1-dB  b2-min   n1    e2-neg  loess-0.5     c1    1.106    2.439    0.821    1.471     0.806     1.435 reproduced reproduced
 r1-dB  b2-min   n1    e2-neg  loess-0.5     c2    1.075    2.439    0.812    1.471     0.805     1.435 reproduced reproduced
 r1-dB  b2-min   n1    e2-pos       none     c1    0.886    1.829    0.861    1.937     0.455     1.327 reproduced  partially
 r1-dB  b2-min   n1    e2-pos       none     c2    0.211    1.829    0.240    1.937     0.106     1.327  partially  partially
 r1-dB  b2-min   n1    e2-pos loess-0.25     c1    0.944    2.583    0.713    1.650     0.568     1.291 reproduced reproduced
 r1-dB  b2-min   n1    e2-pos loess-0.25     c2    0.917    2.583    0.708    1.650     0.560     1.291 reproduced reproduced
 r1-dB  b2-min   n1    e2-pos  loess-0.5     c1    1.146    2.439    0.809    1.507     0.795     1.399 reproduced reproduced
 r1-dB  b2-min   n1    e2-pos  loess-0.5     c2    1.122    2.439    0.806    1.507     0.794     1.399 reproduced reproduced
 r1-dB  b2-min   n2        e1       none     c1    0.885    1.829    0.596    1.829     0.454     1.327 reproduced reproduced
 r1-dB  b2-min   n2        e1       none     c2    0.216    1.829    0.199    1.829     0.108     1.327 reproduced reproduced
 r1-dB  b2-min   n2        e1 loess-0.25     c1    0.928    1.829    0.617    1.578     0.569     1.291 reproduced reproduced
 r1-dB  b2-min   n2        e1 loess-0.25     c2    0.899    1.829    0.587    1.578     0.564     1.291 reproduced reproduced
 r1-dB  b2-min   n2        e1  loess-0.5     c1    1.117    2.439    0.823    1.471     0.785     1.399 reproduced reproduced
 r1-dB  b2-min   n2        e1  loess-0.5     c2    1.087    2.439    0.813    1.471     0.784     1.399 reproduced reproduced

w1: combinations by outcome {'reproduced': 182, 'partially': 28, 'not': 6} of 216 ; with n1 only: {'reproduced': 140, 'partially': 16, 'not': 6} ; with n2 only: {'reproduced': 42, 'partially': 12}
  1Px4: 0.078 to 1.146 mm, median 0.890; not measurable (NaN) in 0
  5Px4: 0.072 to 0.929 mm, median 0.681; not measurable (NaN) in 0
  10Px4: 0.106 to 0.899 mm, median 0.566; not measurable (NaN) in 0
  by smoothing: {'loess-0.25': {'not': 0, 'partially': 3, 'reproduced': 69}, 'loess-0.5': {'not': 0, 'partially': 7, 'reproduced': 65}, 'none': {'not': 6, 'partially': 18, 'reproduced': 48}}
  by background: {'b1': {'not': 2, 'partially': 9, 'reproduced': 61}, 'b2-dark': {'not': 2, 'partially': 10, 'reproduced': 60}, 'b2-min': {'not': 2, 'partially': 9, 'reproduced': 61}}

w2: combinations by outcome {'reproduced': 71, 'partially': 28, 'not': 9} of 108 ; with n1 only: {'reproduced': 50, 'partially': 25, 'not': 6} ; with n2 only: {'reproduced': 21, 'partially': 3, 'not': 3}
  1Px4: 1.363 to 2.762 mm, median 2.367; not measurable (NaN) in 0
  5Px4: 1.471 to 2.260 mm, median 1.722; not measurable (NaN) in 0
  10Px4: 1.076 to 2.116 mm, median 1.417; not measurable (NaN) in 0
  by smoothing: {'loess-0.25': {'not': 0, 'partially': 7, 'reproduced': 29}, 'loess-0.5': {'not': 0, 'partially': 3, 'reproduced': 33}, 'none': {'not': 9, 'partially': 18, 'reproduced': 9}}
  by background: {'b1': {'not': 3, 'partially': 9, 'reproduced': 24}, 'b2-dark': {'not': 3, 'partially': 10, 'reproduced': 23}, 'b2-min': {'not': 3, 'partially': 9, 'reproduced': 24}}

w1 by center reading: {'c1': {'not': 0, 'partially': 12, 'reproduced': 96}, 'c2': {'not': 6, 'partially': 16, 'reproduced': 86}}
Variant b2-fp (not a reading of the claims file): w1 {'reproduced': 60, 'partially': 8, 'not': 4} of 72 ; w2 {'reproduced': 28, 'partially': 6, 'not': 2} of 36
In [19]:
# The primary reading (r2, b1, n1, e1), per treatment, under both center readings, with the positions of the minima.
data = table("r2", "b1")
rows = []
for sm in cancan.SMOOTHING:
    for cen in cancan.CENTERS:
        for t in TREATMENTS:
            pos, P = data[t]
            Q = cancan.mean_profile(P, "e1", "n1")
            if sm != "none":
                Q = cancan.loess(pos, Q, float(sm.split("-")[1]))
            ci, mins = cancan.center_index(Q, cen), cancan.side_minima(Q)
            start = ci[0] if len(ci) == 1 else (ci[0] if Q[ci[0]] >= Q[ci[1]] else ci[1])
            rows.append({"smoothing": sm, "center": cen, "treatment": t, "peak_pct": round(cancan.center_value(Q, ci)[0], 1),
                         "peak_mm": round(float(np.mean(pos[ci])), 2),
                         "minima_mm": [round(pos[i], 2) for i in mins], "base_pct": round(cancan.trough_value(Q, mins, "t1")[0], 1),
                         "w1_mm": round(cancan.half_max_width(pos, Q, start, cancan.center_value(Q, ci)[0], cancan.trough_value(Q, mins, "t1")[0]), 3),
                         "w2_mm": round(pos[mins[1]] - pos[mins[0]], 3)})
print(pd.DataFrame(rows).to_string(index=False))
# Per dish, w2 on each dish's own LOESS(0.5) normalized profile, to show the spread between dishes.
rows = []
for t in TREATMENTS:
    pos, P = data[t]
    for d, p in zip(dishes[t], P):
        q = cancan.loess(pos, p / cancan.periphery(p, "e1")[0] * 100, 0.5)
        mins = cancan.side_minima(q)
        rows.append({"treatment": t, "dish": d["dish"], "w2_loess0.5_mm": round(pos[mins[1]] - pos[mins[0]], 3)})
print(pd.DataFrame(rows).to_string(index=False))
 smoothing center treatment  peak_pct  peak_mm     minima_mm  base_pct  w1_mm  w2_mm
      none     c1      1Px4     124.0     0.00 [-0.54, 0.82]      88.6  1.051  1.363
      none     c1      5Px4      86.7     0.00 [-0.75, 0.97]      40.1  0.545  1.722
      none     c1     10Px4      58.7     0.00 [-0.54, 0.61]      20.9  0.488  1.148
      none     c2      1Px4     139.5     0.11 [-0.54, 0.82]      88.6  0.591  1.363
      none     c2      5Px4      91.7    -0.04 [-0.75, 0.97]      40.1  0.511  1.722
      none     c2     10Px4      70.8     0.04 [-0.54, 0.61]      20.9  0.437  1.148
loess-0.25     c1      1Px4     126.6     0.00  [-0.9, 0.82]      94.3  0.800  1.722
loess-0.25     c1      5Px4      81.3     0.00   [-0.9, 0.9]      44.9  0.654  1.793
loess-0.25     c1     10Px4      56.6     0.00 [-0.68, 0.68]      22.9  0.560  1.363
loess-0.25     c2      1Px4     127.2     0.04  [-0.9, 0.82]      94.3  0.791  1.722
loess-0.25     c2      5Px4      81.7    -0.04   [-0.9, 0.9]      44.9  0.649  1.793
loess-0.25     c2     10Px4      57.8     0.04 [-0.68, 0.68]      22.9  0.546  1.363
 loess-0.5     c1      1Px4     118.8     0.00 [-0.82, 1.54]      97.3  0.992  2.367
 loess-0.5     c1      5Px4      69.6     0.00 [-0.82, 0.82]      50.5  0.863  1.650
 loess-0.5     c1     10Px4      43.4     0.00 [-0.68, 0.75]      28.0  0.817  1.435
 loess-0.5     c2      1Px4     119.1     0.11 [-0.82, 1.54]      97.3  0.980  2.367
 loess-0.5     c2      5Px4      69.7    -0.04 [-0.82, 0.82]      50.5  0.860  1.650
 loess-0.5     c2     10Px4      43.5     0.04 [-0.68, 0.75]      28.0  0.813  1.435
treatment  dish  w2_loess0.5_mm
     1Px4    23           2.367
     1Px4    24           1.722
     1Px4    25           1.865
     5Px4     9           1.435
     5Px4    20           1.578
     5Px4    22           1.650
    10Px4     4           1.578
    10Px4    11           1.435
    10Px4    18           1.363
In [20]:
# Variant: ROI extent from the footprints (no pixel size), r2, b1, n1, e1, c1; widths in mm at the assumed scale.
data = table("r2", "b1", "geometric")
for sm in cancan.SMOOTHING:
    w = {}
    for t in TREATMENTS:
        pos, P = data[t]
        Q = cancan.mean_profile(P, "e1", "n1")
        if sm != "none":
            Q = cancan.loess(pos, Q, float(sm.split("-")[1]))
        ci, mins = cancan.center_index(Q, "c1"), cancan.side_minima(Q)
        start = ci[0] if Q[ci[0]] >= Q[ci[1]] else ci[1]
        w[t] = (cancan.half_max_width(pos, Q, start, cancan.center_value(Q, ci)[0], cancan.trough_value(Q, mins, "t1")[0]),
                pos[mins[1]] - pos[mins[0]])
    print(sm, {t: (round(a, 3), round(b, 3)) for t, (a, b) in w.items()}, "(w1, w2)")
none {'1Px4': (np.float64(0.951), np.float64(2.311)), '5Px4': (np.float64(0.569), np.float64(2.035)), '10Px4': (np.float64(0.475), np.float64(1.097))} (w1, w2)
loess-0.25 {'1Px4': (np.float64(0.795), np.float64(1.699)), '5Px4': (np.float64(0.637), np.float64(1.764)), '10Px4': (np.float64(0.544), np.float64(1.303))} (w1, w2)
loess-0.5 {'1Px4': (np.float64(0.93), np.float64(2.243)), '5Px4': (np.float64(0.85), np.float64(1.561)), '10Px4': (np.float64(0.797), np.float64(1.508))} (w1, w2)

Result: reproduced. On the primary reading (r2, b1, n1, e1) the width at half height above the least affected area (w1) is 1.051, 0.545 and 0.488 mm for 4, 20 and 40 packets; with LOESS of span 0.25 it is 0.800, 0.654 and 0.560 mm, and with span 0.5 0.992, 0.863 and 0.817 mm. Unsmoothed, w1 depends on the center reading: under c2 it is 0.591, 0.511 and 0.437 mm, still in order, while the smoothed widths change by less than 0.02 mm. Over the 216 combinations w1 decreases in 182, 40 is narrower than 4 but 20 out of order in 28, and the 6 others, all unsmoothed with c2, are out of order. The distance between the minima (w2) does not depend on the center reading and is counted once per combination: it decreases in 71 of 108, with 28 partial and 9 out of order, all 9 unsmoothed. On the primary reading it is 2.367, 1.650 and 1.435 mm with LOESS 0.5, and 1.363, 1.722 and 1.148 mm unsmoothed. Per dish, w2 on LOESS 0.5 is 1.722 to 2.367 mm for 1Px4, 1.435 to 1.650 for 5Px4 and 1.363 to 1.578 for 10Px4: 1Px4 and 10Px4 do not overlap, 5Px4 and 10Px4 do, and the criterion asks for no test. With the ROI extent from the footprints w1 keeps the order under all three smoothings. The widths in mm rest on the assumed pixel size; their order does not.

Summary¶

A failure to reproduce here may be a failure of this attempt, not of the paper: see the reasoning and the differences from the paper's method.

Claim Data Outcome
fig6a-more-packets-more-uptake dandiset reproduced
fig6b-peak-relative-to-periphery dandiset partially reproduced
fig6-trough-half-of-center dandiset partially reproduced
fig6-peak-narrower dandiset reproduced
fig4-bipolar-cancellation, fig4-twenty-fold-cancellation dandiset blocked: data missing
fig5a-packet-without-rotation, fig5bc-rotation-focuses-center dandiset blocked: data missing
fig7ab-amplitude-scales-uptake, fig7-targeting-independent-of-amplitude, fig7-targeting-independent-of-array-size dandiset blocked: data missing
fig8-packet2-best-targeting, fig8b-normalized-center, fig8-cancellation-predicts-targeting dandiset blocked: data missing
fig9-gap-fades-periphery, fig9b-six-fold-targeting dandiset blocked: data missing
fig3-field-simulation with inputs blocked: data missing

How the analysis differs from the paper's. The paper measured the images in MetaMorph with ROIs that it does not deposit; here the ROIs are placed by code on the detected electrode footprints. The layout, background, center, periphery and normalization order are not fixed by the paper, and every reading the claims file names is run. The files hold no dark frame, so the background reading b2 subtracts dark offsets estimated from the darkest blocks of each image or the darkest pixel near the array; the electrode footprints hold a faint monolayer and are not used as a background. The pixel size is assumed (0.65 um); a variant that places the ROIs from the footprints alone gives the same outcomes. The dandiset has 3 dishes per treatment, against 3 to 4 in the Fig. 6 legend.

What was found about the dandiset.

  • It holds only the Fig. 6 experiment (9 dishes at 3.2 kV). The single-cell data of Fig. 4 and the monolayer data of Figs. 5, 7, 8 and 9 are not in it.
  • The files give no pixel size, no electrode positions and no ROIs, and do not say which array was used; the array is the small one by its geometry at the camera scale. The dark electrode footprints are 1.66 to 2.09 mm across at that scale, wider than the 1.5 mm needles.
  • The species is given as NCBITaxon 9913 (Bos taurus) for CHO cells (Chinese hamster), the age as "P1D", and the archive's summary counts 18 subjects for 9 files and 9 subject ids.
  • Two pairs of dishes share a session start time to the second, one pair across treatments; the images differ, so no recording is deposited twice.
  • The ImageSeries has unit "n.a." and a rate of 2 Hz for a single frame, and the "200ms" in the file names is not explained.
In [21]:
print(f"Notebook ran in {time.time() - started:.0f} s")
Notebook ran in 271 s