Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ see the [Concepts Guide](https://lineage.readthedocs.io/en/stable/concepts.html)

```python
import logging
logging.basicConfig(level=logging.INFO, format='%(message)s')

logging.basicConfig(level=logging.INFO, format="%(message)s")
```

To try these examples, first generate some sample data:
Expand Down
2 changes: 0 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# lineage documentation build configuration file
#
Expand Down
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta:__legacy__"

[tool.ruff]
# versioneer files are vendored / generated
extend-exclude = ["versioneer.py", "src/lineage/_version.py"]

# https://github.com/astral-sh/ruff/issues/465#issuecomment-1783684428
[tool.ruff.lint]
extend-select = ["I"]
33 changes: 14 additions & 19 deletions src/lineage/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

__version__ = _version.get_versions()["version"]

__all__ = ["Lineage", "Individual", "SyntheticRelatedGenerator", "__version__"]
__all__ = ["Individual", "Lineage", "SyntheticRelatedGenerator", "__version__"]

logger = logging.getLogger(__name__)

Expand All @@ -36,7 +36,7 @@ def __init__(
output_dir="output",
resources_dir="resources",
parallelize=False,
processes=os.cpu_count(),
processes=None,
):
"""Initialize a ``Lineage`` object.

Expand All @@ -48,9 +48,12 @@ def __init__(
name / path of resources directory
parallelize : bool
utilize multiprocessing to speedup calculations
processes : int
processes to launch if multiprocessing
processes : int, optional
processes to launch if multiprocessing; defaults to ``os.cpu_count()``
"""
if processes is None:
processes = os.cpu_count()

self._output_dir = output_dir
self._resources_dir = resources_dir
self._resources = Resources(resources_dir=resources_dir)
Expand Down Expand Up @@ -168,9 +171,7 @@ def find_discordant_snps(
save_df_as_csv(
df,
self._output_dir,
"discordant_snps_{}_{}_GRCh37.csv".format(
individual1.get_var_name(), individual2.get_var_name()
),
f"discordant_snps_{individual1.get_var_name()}_{individual2.get_var_name()}_GRCh37.csv",
comment=self._get_csv_header(),
prepend_info=False,
)
Expand Down Expand Up @@ -236,11 +237,7 @@ def find_discordant_snps(
save_df_as_csv(
df,
self._output_dir,
"discordant_snps_{}_{}_{}_GRCh37.csv".format(
individual1.get_var_name(),
individual2.get_var_name(),
individual3.get_var_name(),
),
f"discordant_snps_{individual1.get_var_name()}_{individual2.get_var_name()}_{individual3.get_var_name()}_GRCh37.csv",
comment=self._get_csv_header(),
prepend_info=False,
)
Expand Down Expand Up @@ -372,7 +369,7 @@ def find_shared_dna(

# generate a list of dynamically named columns for each individual's genotype
# (e.g., genotype0, genotype1, etc).
cols = [f"genotype{str(i)}" for i in range(len(individuals))]
cols = [f"genotype{i}" for i in range(len(individuals))]

# set the reference SNPs to compare to be that of the first individual
df = individuals[0].snps
Expand All @@ -392,7 +389,7 @@ def find_shared_dna(
tasks = []
chroms_to_drop = []
for chrom in df["chrom"].unique():
if chrom not in genetic_map_dfs.keys():
if chrom not in genetic_map_dfs:
chroms_to_drop.append(chrom)
continue

Expand Down Expand Up @@ -553,7 +550,7 @@ def output_csv(df, file, float_format="%.2f"):
individuals_filename = individuals_filename[:-1]
individuals_plot_title = individuals_plot_title[:-3]

cM = "{:.2f}".format(cM_threshold).replace(".", "p")
cM = f"{cM_threshold:.2f}".replace(".", "p")
filename_details = (
f"{individuals_filename}_{cM}cM_{snp_threshold}snps_GRCh37_{genetic_map}"
)
Expand Down Expand Up @@ -737,7 +734,7 @@ def _compute_shared_dna(self, task):
snp_threshold = task["snp_threshold"]
one_x_chrom = task["one_x_chrom"]

if "one_chrom_match" in df.keys():
if "one_chrom_match" in df:
match_col = "one_chrom_match"
else:
match_col = "two_chrom_match"
Expand Down Expand Up @@ -816,9 +813,8 @@ def _compute_shared_dna(self, task):
cMs_match_segment = c[:, 1] - c[:, 0]

discrepant_snps_passed = pd.Index([], name="rsid")
counter = 0
# save matches for this chromosome
for x in matches_passed:
for counter, x in enumerate(matches_passed):
d = {
"chrom": chrom,
"start": df.loc[(df["chrom"] == chrom)].iloc[x[0]].pos,
Expand All @@ -840,7 +836,6 @@ def _compute_shared_dna(self, task):
)

shared_dna.append(d)
counter += 1
return {"shared_dna": shared_dna, "discrepant_snps": discrepant_snps_passed}

def _remap_snps_to_GRCh37(self, individuals):
Expand Down
2 changes: 1 addition & 1 deletion src/lineage/individual.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def _get_defined_kwargs(self, callable, kwargs):
return {k: kwargs[k] for k in kwargs if k in sig.parameters}

def __repr__(self):
return "Individual({!r})".format(self._name)
return f"Individual({self._name!r})"

@property
def name(self):
Expand Down
4 changes: 2 additions & 2 deletions src/lineage/visualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def plot_chromosomes(one_chrom_match, two_chrom_match, cytobands, path, title, b
chrom_spacing = 1

# Decide which chromosomes to use
chromosome_list = ["chr%s" % i for i in range(1, 23)]
chromosome_list = [f"chr{i}" for i in range(1, 23)]
chromosome_list.append("chrY")
chromosome_list.append("chrX")

Expand Down Expand Up @@ -147,7 +147,7 @@ def plot_chromosomes(one_chrom_match, two_chrom_match, cytobands, path, title, b

ax.set_title(title, fontsize=14, fontweight="bold")
plt.xlabel("Build " + str(build) + " Chromosome Position", fontsize=10)
logger.info("Saving {}".format(os.path.relpath(path)))
logger.info(f"Saving {os.path.relpath(path)}")
plt.tight_layout()

with atomic_write(path, mode="wb", overwrite=True) as f:
Expand Down
3 changes: 1 addition & 2 deletions tests/test_config.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""
Configuration file for test resource sizes and shared gene counts.
This file is automatically generated by running update_resource_sizes.py.
Do not edit manually; rerun update_resource_sizes.py to update values.
These values size the mocked resource data used when downloads are disabled.
"""

# Resource sizes for hg19 data
Expand Down
4 changes: 2 additions & 2 deletions tests/test_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,13 +273,13 @@ def test_crossovers_per_chrom_affects_sibling_pattern(self):
"""Test that crossovers_per_chrom parameter affects sibling generation."""
# Low crossovers should produce longer segments
gen_low = SyntheticRelatedGenerator(build=37, seed=42, crossovers_per_chrom=0.5)
sib1_low, sib2_low = gen_low.generate_sibling_pair(num_snps=5000)
sib1_low, _sib2_low = gen_low.generate_sibling_pair(num_snps=5000)

# High crossovers should produce more variation
gen_high = SyntheticRelatedGenerator(
build=37, seed=42, crossovers_per_chrom=3.0
)
sib1_high, sib2_high = gen_high.generate_sibling_pair(num_snps=5000)
sib1_high, _sib2_high = gen_high.generate_sibling_pair(num_snps=5000)

# Just verify both run without error and produce valid output
self.assertEqual(len(sib1_low), len(sib1_high))
Expand Down
117 changes: 42 additions & 75 deletions tests/test_lineage.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,23 +139,32 @@ def run_find_shared_dna_test(self, f, HapMap2=True, test_case="default", **kwarg
cytoband = self._generate_test_cytoBand_hg19()
kg, kgXref = self._generate_test_gene_dfs(test_case=test_case, **kwargs)

with patch(
genetic_map_patch,
Mock(return_value=genetic_map),
):
with patch(
with (
patch(
genetic_map_patch,
Mock(return_value=genetic_map),
),
patch(
"lineage.resources.Resources.get_cytoBand_hg19",
Mock(return_value=cytoband),
):
with patch(
"lineage.resources.Resources.get_knownGene_hg19",
Mock(return_value=kg),
):
with patch(
"lineage.resources.Resources.get_kgXref_hg19",
Mock(return_value=kgXref),
):
f()
),
patch(
"lineage.resources.Resources.get_knownGene_hg19",
Mock(return_value=kg),
),
patch(
"lineage.resources.Resources.get_kgXref_hg19",
Mock(return_value=kgXref),
),
):
f()

def _assert_shared_genes(self, shared_genes, test_case, key="len1"):
if self.downloads_enabled:
# downloaded gene resources are updated upstream, so only verify genes were found
self.assertGreater(len(shared_genes), 0)
else:
self.assertEqual(len(shared_genes), SHARED_GENE_COUNTS[test_case][key])

def _assert_exists(self, files, idx):
for i, file in enumerate(files):
Expand Down Expand Up @@ -341,14 +350,8 @@ def _test_find_shared_dna_two_chrom_shared(self):

assert len(d["one_chrom_shared_dna"]) == 1
assert len(d["two_chrom_shared_dna"]) == 1
assert (
len(d["one_chrom_shared_genes"])
== SHARED_GENE_COUNTS["default"]["len1"]
)
assert (
len(d["two_chrom_shared_genes"])
== SHARED_GENE_COUNTS["default"]["len1"]
)
self._assert_shared_genes(d["one_chrom_shared_genes"], "default")
self._assert_shared_genes(d["two_chrom_shared_genes"], "default")
assert len(d["one_chrom_discrepant_snps"]) == 0
assert len(d["two_chrom_discrepant_snps"]) == 0
np.testing.assert_allclose(
Expand All @@ -375,12 +378,8 @@ def _test_find_shared_dna_two_chrom_shared_1000G(self):

assert len(d["one_chrom_shared_dna"]) == 1
assert len(d["two_chrom_shared_dna"]) == 1
assert (
len(d["one_chrom_shared_genes"]) == SHARED_GENE_COUNTS["1000G"]["len1"]
)
assert (
len(d["two_chrom_shared_genes"]) == SHARED_GENE_COUNTS["1000G"]["len1"]
)
self._assert_shared_genes(d["one_chrom_shared_genes"], "1000G")
self._assert_shared_genes(d["two_chrom_shared_genes"], "1000G")
assert len(d["one_chrom_discrepant_snps"]) == 0
assert len(d["two_chrom_discrepant_snps"]) == 0
np.testing.assert_allclose(
Expand Down Expand Up @@ -410,14 +409,8 @@ def _test_find_shared_dna_two_chrom_shared_three_ind(self):

assert len(d["one_chrom_shared_dna"]) == 1
assert len(d["two_chrom_shared_dna"]) == 1
assert (
len(d["one_chrom_shared_genes"])
== SHARED_GENE_COUNTS["default"]["len1"]
)
assert (
len(d["two_chrom_shared_genes"])
== SHARED_GENE_COUNTS["default"]["len1"]
)
self._assert_shared_genes(d["one_chrom_shared_genes"], "default")
self._assert_shared_genes(d["two_chrom_shared_genes"], "default")
assert len(d["one_chrom_discrepant_snps"]) == 0
assert len(d["two_chrom_discrepant_snps"]) == 0
np.testing.assert_allclose(
Expand All @@ -444,14 +437,8 @@ def _test_find_shared_dna_two_chrom_shared_no_output(self):

assert len(d["one_chrom_shared_dna"]) == 1
assert len(d["two_chrom_shared_dna"]) == 1
assert (
len(d["one_chrom_shared_genes"])
== SHARED_GENE_COUNTS["default"]["len1"]
)
assert (
len(d["two_chrom_shared_genes"])
== SHARED_GENE_COUNTS["default"]["len1"]
)
self._assert_shared_genes(d["one_chrom_shared_genes"], "default")
self._assert_shared_genes(d["two_chrom_shared_genes"], "default")
assert len(d["one_chrom_discrepant_snps"]) == 0
assert len(d["two_chrom_discrepant_snps"]) == 0
np.testing.assert_allclose(
Expand Down Expand Up @@ -480,10 +467,7 @@ def _test_find_shared_dna_one_chrom_shared(self):

assert len(d["one_chrom_shared_dna"]) == 1
assert len(d["two_chrom_shared_dna"]) == 0
assert (
len(d["one_chrom_shared_genes"])
== SHARED_GENE_COUNTS["default"]["len1"]
)
self._assert_shared_genes(d["one_chrom_shared_genes"], "default")
assert len(d["two_chrom_shared_genes"]) == 0
assert len(d["one_chrom_discrepant_snps"]) == 0
assert len(d["two_chrom_discrepant_snps"]) == 0
Expand Down Expand Up @@ -513,10 +497,7 @@ def _test_find_shared_dna_one_chrom_shared_three_ind(self):

assert len(d["one_chrom_shared_dna"]) == 1
assert len(d["two_chrom_shared_dna"]) == 0
assert (
len(d["one_chrom_shared_genes"])
== SHARED_GENE_COUNTS["default"]["len1"]
)
self._assert_shared_genes(d["one_chrom_shared_genes"], "default")
assert len(d["two_chrom_shared_genes"]) == 0
assert len(d["one_chrom_discrepant_snps"]) == 0
assert len(d["two_chrom_discrepant_snps"]) == 0
Expand Down Expand Up @@ -555,14 +536,10 @@ def _test_find_shared_dna_X_chrom_two_individuals_male(self):

assert len(d["one_chrom_shared_dna"]) == 1 # PAR1, non-PAR, PAR2
assert len(d["two_chrom_shared_dna"]) == 1 # PAR1
assert (
len(d["one_chrom_shared_genes"])
== SHARED_GENE_COUNTS["X_chrom_male"]["len2"]
)
assert (
len(d["two_chrom_shared_genes"])
== SHARED_GENE_COUNTS["X_chrom_male"]["len1"]
self._assert_shared_genes(
d["one_chrom_shared_genes"], "X_chrom_male", key="len2"
)
self._assert_shared_genes(d["two_chrom_shared_genes"], "X_chrom_male")
assert len(d["one_chrom_discrepant_snps"]) == 0
assert len(d["two_chrom_discrepant_snps"]) == 0
np.testing.assert_allclose(
Expand Down Expand Up @@ -599,13 +576,9 @@ def _test_find_shared_dna_X_chrom_two_individuals_female(self):

assert len(d["one_chrom_shared_dna"]) == 1 # PAR1, non-PAR, PAR2
assert len(d["two_chrom_shared_dna"]) == 1 # PAR1, non-PAR, PAR2
assert (
len(d["one_chrom_shared_genes"])
== SHARED_GENE_COUNTS["X_chrom_female"]["len1"]
)
assert (
len(d["two_chrom_shared_genes"])
== SHARED_GENE_COUNTS["X_chrom_female"]["len2"]
self._assert_shared_genes(d["one_chrom_shared_genes"], "X_chrom_female")
self._assert_shared_genes(
d["two_chrom_shared_genes"], "X_chrom_female", key="len2"
)
assert len(d["one_chrom_discrepant_snps"]) == 0
assert len(d["two_chrom_discrepant_snps"]) == 0
Expand Down Expand Up @@ -638,14 +611,8 @@ def _test_find_shared_dna_two_chrom_shared_discrepant_snps(self):

assert len(d["one_chrom_shared_dna"]) == 1
assert len(d["two_chrom_shared_dna"]) == 1
assert (
len(d["one_chrom_shared_genes"])
== SHARED_GENE_COUNTS["default"]["len1"]
)
assert (
len(d["two_chrom_shared_genes"])
== SHARED_GENE_COUNTS["default"]["len1"]
)
self._assert_shared_genes(d["one_chrom_shared_genes"], "default")
self._assert_shared_genes(d["two_chrom_shared_genes"], "default")
assert len(d["one_chrom_discrepant_snps"]) == 0
assert len(d["two_chrom_discrepant_snps"]) == 2
np.testing.assert_allclose(
Expand Down
Loading
Loading