Pairwise Compare
Compare the rows of a tidy pandas dataframe in pairs, for example the correlation between the profiles of wells that differ in their cell line. You choose which columns must be different and which must be the same between the two rows of a pair, and get one result per pair.
The problem it solves: as an analyst of image-based profiles, I need the similarity of every pair of samples that differ in one way and match in another (for example, wells of different cell lines on the same plate), so that I can see how replicates agree and how groups differ, because the average of a group hides how its samples relate.
flowchart LR
subgraph U2OS["U2OS (plate P1)"]
A1["well A1"]
A2["well A2"]
end
subgraph HeLa["HeLa (plate P1)"]
B1["well B1"]
B2["well B2"]
end
A1 -.-|"r = 1.00"| A2
B1 -.-|"r = 1.00"| B2
A1 ---|"r = 0.90"| B1
A2 ---|"r = 0.90"| B1
A1 ---|"r = 0.92"| B2
A2 ---|"r = 0.92"| B2
Each line is one result: the Pearson correlation r of two wells.
Solid lines join wells of different cell lines, and dashed lines join the replicate wells of one cell line.
The example notebook uses a dataset from the nf1_schwann_cell_painting_data.
Most of the development is in the src folder.
Quick start
Take four wells of one plate, with the metadata that identify them and four measured features:
import pandas as pd
from pairwise_compare import compare_pairs
wells = pd.DataFrame(
{
"plate": ["P1", "P1", "P1", "P1"],
"cell_line": ["U2OS", "U2OS", "HeLa", "HeLa"],
"well": ["A1", "A2", "B1", "B2"],
"area": [10.0, 11.0, 20.0, 19.0],
"intensity": [5.0, 5.5, 2.0, 2.5],
"texture": [0.1, 0.3, 0.9, 0.7],
"eccentricity": [0.5, 0.4, 0.8, 0.9],
}
)
compare_pairs(
wells,
different=["cell_line", "well"], # the two wells of a pair are of different cell lines
features=["area", "intensity", "texture", "eccentricity"],
same=["plate"], # and are on the same plate
)
The result has one row per pair of wells, with their Pearson correlation across the features:
| pearsons_correlation | plate__group0 | plate__group1 | cell_line__group0 | cell_line__group1 | well__group0 | well__group1 |
|---|---|---|---|---|---|---|
| 0.90 | P1 | P1 | HeLa | U2OS | B1 | A1 |
| 0.90 | P1 | P1 | HeLa | U2OS | B1 | A2 |
| 0.92 | P1 | P1 | HeLa | U2OS | B2 | A1 |
| 0.92 | P1 | P1 | HeLa | U2OS | B2 | A2 |
This call compares wells of different cell lines (the solid lines in the picture). Wells of the same cell line are not compared with each other.
To compare the replicate wells of each cell line instead (the dashed lines), keep the cell line the same and ask for different wells:
compare_pairs(
wells,
different=["well"], # the two wells of a pair are different wells
features=["area", "intensity", "texture", "eccentricity"],
same=["plate", "cell_line"], # of the same cell line on the same plate
)
| pearsons_correlation | plate__group0 | plate__group1 | cell_line__group0 | cell_line__group1 | well__group0 | well__group1 |
|---|---|---|---|---|---|---|
| 1.00 | P1 | P1 | HeLa | HeLa | B1 | B2 |
| 1.00 | P1 | P1 | U2OS | U2OS | A1 | A2 |
Each column must either match (same) or differ (different), so one call gives one kind of pair.
To get every pair of different wells in one call, use different=["well"] and same=["plate"].
The results only hold the columns you name, so add the cell line to each pair afterwards by looking it up from its well.
Installation
You can install the package with either of the following commands:
pip install pairwise-compare
uv add pairwise-compare
Data format
The dataframe is tidy, as described by Wickham (2014): one row per sample (for example, one well) and one column per variable.
- Group columns hold the metadata that define which rows are compared, such as the plate, the cell line, or the well.
They are the columns listed in
different,sameanddrop. - Feature columns hold the numbers that are compared, such as measurements of the cell morphology.
They are listed in
featuresand must be numeric.
All other columns are ignored.
Which rows are compared
- Two rows are compared when they have a different value in every
differentcolumn and, ifsameis given, the same value in everysamecolumn. differentneeds at least two columns, or one column and at least onesamecolumn. A column cannot be in both.- Each pair is compared once: the pair (A, B) is not repeated as (B, A), and a row is not compared with itself.
group0andgroup1in the output are the two rows of a pair. For asamecolumn both have the same value.dropleaves columns ofsameordifferentout of the results. They still decide which rows are compared. For example,drop=["Metadata_Well"]removes the well columns and keeps the others.
Missing values
- A row with a missing value in a
sameordifferentcolumn is left out of the comparisons, with a warning that says how many rows and which columns. - A row with a missing value in a feature column is kept, and the results of its comparisons are missing (NaN), with a warning.
- Missing values in other columns are ignored.
- A row with the same value in every feature has no Pearson correlation (NaN), with a warning.
Other comparisons
compare_pairs uses the Pearson correlation by default. To compare the rows another way, pass a comparator, which is a class that inherits from Comparator (see Comparator.py and the example in test_comparator.py):
__call__(group0, group1)compares the feature values of two groups of rows and saves the results.save_groups(group_columns, **groups)saves the values of the group columns of the two groups.
A comparator keeps the results of every comparison it has made, so use a new one for each call to compare_pairs.
Citation
If you use Pairwise Compare, please cite it with the metadata in CITATION.cff (the "Cite this repository" button of GitHub uses it). The tidy data format is described in:
Wickham, H. (2014). Tidy Data. Journal of Statistical Software, 59(10), 1-23.
Development
See CONTRIBUTING.md for how to run the tests and make a release.
Metadata
Release files for pairwise-compare 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| pairwise_compare-0.3.0.tar.gz | 1.1 MB | With provenance | Details for pairwise_compare-0.3.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| pairwise_compare-0.3.0-py3-none-any.whl | Python 3 | none | any | Details for pairwise_compare-0.3.0-py3-none-any.whl |
Total release size: 1.1 MB
Release files / pairwise_compare-0.3.0.tar.gz
| Download URL | pairwise_compare-0.3.0.tar.gz |
|---|---|
| Size | 1.1 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e956bf913c1b724446ae85ecfeacf03efd0dd87efe5d880c6af7176226304f05
|
|
BLAKE2b-256 checksum How to use checksums |
f971f2b3db0d7243ea8c12876316b989a14919e207999c3b095553e0b57ea724
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.
Transparency logRelease files / pairwise_compare-0.3.0-py3-none-any.whl
| Download URL | pairwise_compare-0.3.0-py3-none-any.whl |
|---|---|
| Size | 12.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bcdc44851193256f18b489a4c5a14a3ee5c8ef7f792342c4466e5b9cdf7bf4cf
|
|
BLAKE2b-256 checksum How to use checksums |
4ef2a195ba4001a0abbd97ea2e1cfdf40d18a003af212ecc7a87decae7c3276c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.
Transparency logSecurity for pairwise-compare 0.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for pairwise_compare-0.3.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.