Comments (1)
🚀 Here's the PR! #158
7cebf31c33
)Tip
I can email you next time I complete a pull request if you set up your email here!
Actions (click)
- ↻ Restart Sweep
Step 1: 🔎 Searching
I found the following snippets in your repository. I will now analyze these snippets and come up with a plan.
Some code snippets I think are relevant in decreasing order of relevance (click to expand). If some file is missing from here, you can mention the path in the ticket description.
Lines 1 to 3065 in 3d5b908
<?php | |
declare(strict_types=1); | |
namespace Dna\Snps; | |
use Countable; | |
use Dna\Resources; | |
use Dna\Snps\IO\IO; | |
use Dna\Snps\IO\Reader; | |
use Dna\Snps\IO\Writer; | |
use Iterator; | |
// You may need to find alternative libraries for numpy, pandas, and snps in PHP, as these libraries are specific to Python | |
// For numpy, consider using a library such as MathPHP: https://github.com/markrogoyski/math-php | |
// For pandas, you can use DataFrame from https://github.com/aberenyi/php-dataframe, though it is not as feature-rich as pandas | |
// For snps, you'll need to find a suitable PHP alternative or adapt the Python code to PHP | |
// import copy // In PHP, you don't need to import the 'copy' module, as objects are automatically copied when assigned to variables | |
use Dna\Snps\Ensembl; | |
use Dna\Snps\IO\SnpFileReader; | |
use Dna\Snps\Analysis\BuildDetector; | |
use Dna\Snps\Analysis\ClusterOverlapCalculator; | |
class SNPs implements Countable, Iterator | |
class SNPs implements Countable, Iterator | |
{ | |
private array $_source = []; | |
private array $_snps = []; | |
private int $_build = 0; | |
private ?bool $_phased = null; | |
private ?bool $_build_detected = null; | |
private ?Resources $_resources = null; | |
private ?string $_chip = null; | |
private ?string $_chip_version = null; | |
private ?string $_cluster = null; | |
private int $_position = 0; | |
private array $_keys = []; | |
private array $_duplicate = []; | |
private array $_discrepant_XY = []; | |
private array $_heterozygous_MT = []; | |
private DataFrame $dataFrame; | |
private SNPAnalysis $snpAnalysis; | |
private MathOperations $mathOperations; | |
/** | |
* SNPs constructor. | |
* | |
* @param string $file Input file path | |
* @param bool $only_detect_source Flag to indicate whether to only detect the source | |
* @param bool $assign_par_snps Flag to indicate whether to assign par_snps | |
* @param string $output_dir Output directory path | |
* @param string $resources_dir Resources directory path | |
* @param bool $deduplicate Flag to indicate whether to deduplicate | |
* @param bool $deduplicate_XY_chrom Flag to indicate whether to deduplicate XY chromosome | |
* @param bool $deduplicate_MT_chrom Flag to indicate whether to deduplicate MT chromosome | |
* @param bool $parallelize Flag to indicate whether to parallelize | |
* @param int $processes Number of processes to use for parallelization | |
* @param array $rsids Array of rsids | |
*/ | |
public function __construct( | |
private $file = "", | |
private bool $only_detect_source = False, | |
private bool $assign_par_snps = False, | |
private string $output_dir = "output", | |
private string $resources_dir = "resources", | |
private bool $deduplicate = True, | |
private bool $deduplicate_XY_chrom = True, | |
private bool $deduplicate_MT_chrom = True, | |
private bool $parallelize = False, | |
private int $processes = 1, // cpu count | |
private array $rsids = [], | |
private $ensemblRestClient = null, | |
) //, $only_detect_source, $output_dir, $resources_dir, $parallelize, $processes) | |
{ | |
// $this->_only_detect_source = $only_detect_source; | |
$this->snpFileReader = new SnpFileReader($this->_resources, $this->ensemblRestClient); | |
$this->buildDetector = new BuildDetector(); | |
$this->clusterOverlapCalculator = new ClusterOverlapCalculator($this->_resources); | |
$this->_source = []; | |
$this->_phased = null; | |
$this->_build = 0; | |
$this->_build_detected = null; | |
$this->_cluster = ""; | |
$this->_chip = ""; | |
$this->_chip_version = ""; | |
$this->_source = []; | |
// $this->_phased = false; | |
$this->_build = 0; | |
$this->_build_detected = false; | |
// $this->_output_dir = $output_dir; | |
$this->_resources = new Resources($resources_dir); | |
// $this->_parallelizer = new Parallelizer($parallelize, $processes); | |
$this->_cluster = ""; | |
$this->_chip = ""; | |
$this->dataFrame = new DataFrame(); | |
$this->snpAnalysis = new SNPAnalysis(); | |
$this->mathOperations = new MathOperations(); | |
$this->_chip_version = ""; | |
$this->ensemblRestClient = $ensemblRestClient ?? new Ensembl("https://api.ncbi.nlm.nih.gov", 1); | |
if (!empty($file)) { | |
$this->readFile(); | |
} | |
} | |
public function count(): int | |
{ | |
return $this->get_count(); | |
} | |
public function current(): SNPs | |
{ | |
return $this->_snps[$this->_position]; | |
} | |
public function key(): int | |
{ | |
return $this->_position; | |
} | |
public function next(): void | |
{ | |
++$this->_position; | |
} | |
public function rewind(): void | |
{ | |
$this->_position = 0; | |
} | |
public function valid(): bool | |
{ | |
return isset($this->_snps[$this->_position]); | |
} | |
/** | |
* Get the SNPs as a DataFrame. | |
* | |
* @return SNPs[] The SNPs array | |
*/ | |
public function filter(callable $callback) | |
{ | |
return array_filter($this->_snps, $callback); | |
} | |
/** | |
* Get the value of the source property. | |
* | |
* @return string | |
* Data source(s) for this `SNPs` object, separated by ", ". | |
*/ | |
public function getSource(): string | |
{ | |
return implode(", ", $this->_source); | |
} | |
public function getAllSources(): array | |
{ | |
return $this->_source; | |
} | |
/** | |
* Magic method to handle property access. | |
* | |
* @param string $name | |
* The name of the property. | |
* | |
* @return mixed | |
* The value of the property. | |
*/ | |
public function __get(string $name) | |
{ | |
$getter = 'get' . ucfirst($name); | |
if (method_exists($this, $getter)) { | |
return $this->$getter(); | |
} | |
return null; // Or throw an exception for undefined properties | |
} | |
public function setSNPs(array $snps) | |
{ | |
$this->_snps = $snps; | |
$this->_keys = array_keys($snps); | |
} | |
// Method readFile has been removed and its functionality is refactored with SnpFileReader class | |
$this->setSNPs($d["snps"]); | |
$this->_source = (strpos($d["source"], ", ") !== false) ? explode(", ", $d["source"]) : [$d["source"]]; | |
$this->_phased = $d["phased"]; | |
$this->_build = $d["build"] ?? null; | |
$this->_build_detected = !empty($d["build"]); | |
// echo "HERE\n"; | |
// var_dump($d["build"]); | |
// var_dump($this->_build_detected); | |
// $this->_cluster = $d["cluster"]; | |
// if not self._snps.empty: | |
// self.sort() | |
// if deduplicate: | |
// self._deduplicate_rsids() | |
// # use build detected from `read` method or comments, if any | |
// # otherwise use SNP positions to detect build | |
// if not self._build_detected: | |
// self._build = self.detect_build() | |
// self._build_detected = True if self._build else False | |
// if not self._build: | |
// self._build = 37 # assume Build 37 / GRCh37 if not detected | |
// else: | |
// self._build_detected = True | |
if (!empty($this->_snps)) { | |
$this->sort(); | |
if ($this->deduplicate) | |
$this->_deduplicate_rsids(); | |
// use build detected from `read` method or comments, if any | |
// otherwise use SNP positions to detect build | |
if (!$this->_build_detected) { | |
$this->_build = $this->detect_build(); | |
$this->_build_detected = $this->_build ? true : false; | |
if (!$this->_build) { | |
$this->_build = 37; // assume Build 37 / GRCh37 if not detected | |
} else { | |
$this->_build_detected = true; | |
} | |
} | |
// if ($this->assign_par_snps) { | |
// $this->assignParSnps(); | |
// $this->sort(); | |
// } | |
// if ($this->deduplicate_XY_chrom) { | |
// if ( | |
// ($this->deduplicate_XY_chrom === true && $this->determine_sex() == "Male") | |
// || ($this->determine_sex(chrom: $this->deduplicate_XY_chrom) == "Male") | |
// ) { | |
// $this->deduplicate_XY_chrom(); | |
// } | |
// } | |
// if ($this->deduplicate_MT_chrom) { | |
// echo "deduping yo...\n"; | |
// $this->deduplicate_MT_chrom(); | |
// } | |
} | |
} | |
// Method readRawData has been removed and its functionality is refactored with SnpFileReader class | |
} | |
/** | |
* Get the SNPs as an array. | |
* | |
* @return array The SNPs array | |
*/ | |
public function getSnps(): array | |
{ | |
return $this->_snps; | |
} | |
/** | |
* Status indicating if build of SNPs was detected. | |
* | |
* @return bool True if the build was detected, False otherwise | |
*/ | |
public function isBuildDetected(): bool | |
{ | |
return $this->_build_detected; | |
} | |
/** | |
* Get the build number associated with the data. | |
* | |
* @return mixed The build number | |
*/ | |
public function getBuild() | |
{ | |
return $this->_build; | |
} | |
public function setBuild($build) | |
{ | |
$this->_build = $build; | |
} | |
/** | |
* Detected deduced genotype / chip array, if any, per computeClusterOverlap. | |
* | |
* @return string Detected chip array, else an empty string. | |
*/ | |
public function getChip() | |
{ | |
if (empty($this->_chip)) { | |
$this->computeClusterOverlap(); | |
} | |
return $this->_chip; | |
} | |
/** | |
* Detected genotype / chip array version, if any, per | |
* computeClusterOverlap. | |
* | |
* Chip array version is only applicable to 23andMe (v3, v4, v5) and AncestryDNA (v1, v2) files. | |
* | |
* @return string Detected chip array version, e.g., 'v4', else an empty string. | |
*/ | |
public function getChipVersion() | |
{ | |
if (!$this->_chip_version) { | |
$this->computeClusterOverlap(); | |
} | |
return $this->_chip_version; | |
} | |
/** | |
* Compute overlap with chip clusters. | |
* | |
* Chip clusters, which are defined in [1]_, are associated with deduced genotype / | |
* chip arrays and DTC companies. | |
* | |
* This method also sets the values returned by the `cluster`, `chip`, and | |
* `chip_version` properties, based on max overlap, if the specified threshold is | |
* satisfied. | |
* | |
* @param float $clusterOverlapThreshold | |
* Threshold for cluster to overlap this SNPs object, and vice versa, to set | |
* values returned by the `cluster`, `chip`, and `chip_version` properties. | |
* | |
* @return array | |
* Associative array with the following keys: | |
* - `companyComposition`: DTC company composition of associated cluster from [1]_ | |
* - `chipBaseDeduced`: Deduced genotype / chip array of associated cluster from [1]_ | |
* - `snpsInCluster`: Count of SNPs in cluster | |
* - `snpsInCommon`: Count of SNPs in common with cluster (inner merge with cluster) | |
* - `overlapWithCluster`: Percentage overlap of `snpsInCommon` with cluster | |
* - `overlapWithSelf`: Percentage overlap of `snpsInCommon` with this SNPs object | |
* | |
* @see https://doi.org/10.1016/j.csbj.2021.06.040 | |
* Chang Lu, Bastian Greshake Tzovaras, Julian Gough, A survey of | |
* direct-to-consumer genotype data, and quality control tool | |
* (GenomePrep) for research, Computational and Structural | |
* Biotechnology Journal, Volume 19, 2021, Pages 3747-3754, ISSN | |
* 2001-0370. | |
*/ | |
// Method computeClusterOverlap has been removed and its functionality is refactored with ClusterOverlapCalculator class | |
]; | |
$keys = array_keys($data); | |
$df = []; | |
foreach ($data['cluster_id'] as $index => $cluster_id) { | |
$entry = ['cluster_id' => $cluster_id]; | |
foreach ($keys as $key) { | |
$entry[$key] = $data[$key][$index]; | |
} | |
$df[] = $entry; | |
} | |
if ($this->build != 37) { | |
// Create a deep copy of the current object | |
$toRemap = clone $this; | |
// Call the remap method on the copied object | |
$toRemap->remap(37); // clusters are relative to Build 37 | |
// Extract "chrom" and "pos" values from snps and remove duplicates | |
$selfSnps = []; | |
foreach ($toRemap->snps as $snp) { | |
if ( | |
!in_array($snp["chrom"], array_column($selfSnps, "chrom")) || | |
!in_array($snp["pos"], array_column($selfSnps, "pos")) | |
) { | |
$selfSnps[] = $snp; | |
} | |
} | |
} else { | |
// Extract "chrom" and "pos" values from snps and remove duplicates | |
$selfSnps = []; | |
foreach ($this->snps as $snp) { | |
if ( | |
!in_array($snp["chrom"], array_column($selfSnps, "chrom")) || | |
!in_array($snp["pos"], array_column($selfSnps, "pos")) | |
) { | |
$selfSnps[] = $snp; | |
} | |
} | |
} | |
$chip_clusters = $this->_resources->get_chip_clusters(); | |
foreach ($df as $cluster => $row) { | |
$cluster_snps = array_filter($chip_clusters, function ($chip_cluster) use ($cluster) { | |
return strpos($chip_cluster['clusters'], $cluster) !== false; | |
}); | |
$df[$cluster]["snps_in_cluster"] = count($cluster_snps); | |
$df[$cluster]["snps_in_common"] = count(array_uintersect($selfSnps, $cluster_snps, function ($a, $b) { | |
return $a["chrom"] == $b["chrom"] && $a["pos"] == $b["pos"] ? 0 : 1; | |
})); | |
} | |
foreach ($df as &$row) { | |
$row["overlap_with_cluster"] = $row["snps_in_common"] / $row["snps_in_cluster"]; | |
$row["overlap_with_self"] = $row["snps_in_common"] / count($selfSnps); | |
} | |
$max_overlap = array_keys($df, max($df))[0]; | |
if ( | |
$df["overlap_with_cluster"][$max_overlap] > $cluster_overlap_threshold | |
&& $df["overlap_with_self"][$max_overlap] > $cluster_overlap_threshold | |
) { | |
$this->_cluster = $max_overlap; | |
$this->_chip = $df["chip_base_deduced"][$max_overlap]; | |
$company_composition = $df["company_composition"][$max_overlap]; | |
if ($this->source === "23andMe" || $this->source === "AncestryDNA") { | |
$i = strpos($company_composition, "v"); | |
if ($i !== false) { | |
$this->_chip_version = substr($company_composition, $i, 2); | |
} | |
} else { | |
error_log("Detected SNPs data source not found in cluster's company composition"); | |
} | |
} | |
return $df; | |
} | |
/** | |
* Discrepant XY SNPs. | |
* | |
* Discrepant XY SNPs are SNPs that are assigned to both the X and Y chromosomes. | |
* | |
* @return array Discrepant XY SNPs | |
*/ | |
public function getDiscrepantXY() | |
{ | |
return $this->_discrepant_XY; | |
} | |
/** | |
* Get the duplicate SNPs. | |
* | |
* A duplicate SNP has the same RSID as another SNP. The first occurrence | |
* of the RSID is not considered a duplicate SNP. | |
* | |
* @return SNPs[] Duplicate SNPs | |
*/ | |
public function getDuplicate() | |
{ | |
return $this->_duplicate; | |
} | |
/** | |
* Count of SNPs. | |
* | |
* @param string $chrom (optional) Chromosome (e.g., "1", "X", "MT") | |
* @return int The count of SNPs for the given chromosome | |
*/ | |
public function get_count($chrom = "") | |
{ | |
return count($this->_filter($chrom)); | |
} | |
protected function _filter($chrom = "") | |
{ | |
if (!empty($chrom)) { | |
$filteredSnps = array_filter($this->_snps, function ($snp) use ($chrom) { | |
return $snp['chrom'] === $chrom; | |
}); | |
return $filteredSnps; | |
} else { | |
return $this->_snps; | |
} | |
} | |
/** | |
* Detect build of SNPs. | |
* | |
* Use the coordinates of common SNPs to identify the build / assembly of a genotype file | |
* that is being loaded. | |
* | |
* Notes: | |
* - rs3094315 : plus strand in 36, 37, and 38 | |
* - rs11928389 : plus strand in 36, minus strand in 37 and 38 | |
* - rs2500347 : plus strand in 36 and 37, minus strand in 38 | |
* - rs964481 : plus strand in 36, 37, and 38 | |
* - rs2341354 : plus strand in 36, 37, and 38 | |
* - rs3850290 : plus strand in 36, 37, and 38 | |
* - rs1329546 : plus strand in 36, 37, and 38 | |
* | |
* Returns detected build of SNPs, else 0 | |
* | |
* References: | |
* 1. Yates et. al. (doi:10.1093/bioinformatics/btu613), | |
* <http://europepmc.org/search/?query=DOI:10.1093/bioinformatics/btu613> | |
* 2. Zerbino et. al. (doi.org/10.1093/nar/gkx1098), https://doi.org/10.1093/nar/gkx1098 | |
* 3. Sherry ST, Ward MH, Kholodov M, Baker J, Phan L, Smigielski EM, Sirotkin K. | |
* dbSNP: the NCBI database of genetic variation. Nucleic Acids Res. 2001 | |
* Jan 1;29(1):308-11. | |
* 4. Database of Single Nucleotide Polymorphisms (dbSNP). Bethesda (MD): National Center | |
* for Biotechnology Information, National Library of Medicine. dbSNP accession: rs3094315, | |
* rs11928389, rs2500347, rs964481, rs2341354, rs3850290, and rs1329546 | |
* (dbSNP Build ID: 151). Available from: http://www.ncbi.nlm.nih.gov/SNP/ | |
*/ | |
// Method detect_build has been removed and its functionality is refactored with BuildDetector class | |
"rs3850290", | |
"rs1329546", | |
]; | |
$df = [ | |
"rs3094315" => [36 => 742429, 37 => 752566, 38 => 817186], | |
"rs11928389" => [36 => 50908372, 37 => 50927009, 38 => 50889578], | |
"rs2500347" => [36 => 143649677, 37 => 144938320, 38 => 148946169], | |
"rs964481" => [36 => 27566744, 37 => 27656823, 38 => 27638706], | |
"rs2341354" => [36 => 908436, 37 => 918573, 38 => 983193], | |
"rs3850290" => [36 => 22315141, 37 => 23245301, 38 => 22776092], | |
"rs1329546" => [36 => 135302086, 37 => 135474420, 38 => 136392261] | |
]; | |
foreach ($this->_snps as $snp) { | |
if (in_array($snp['rsid'], $rsids)) { | |
$build = $lookup_build_with_snp_pos($snp['pos'], $df[$snp['rsid']]); | |
} | |
if ($build) { | |
break; | |
} | |
} | |
return $build; | |
} | |
/** | |
* Convert the SNPs object to a string representation. | |
* | |
* @return string The string representation of the SNPs object | |
*/ | |
public function __toString() | |
{ | |
if (is_string($this->file) && is_file($this->file)) { | |
// If the file path is a string, return SNPs with the basename of the file | |
return "SNPs('" . basename($this->file) . "')"; | |
} else { | |
// If the file path is not a string, return SNPs with <bytes> | |
return "SNPs(<bytes>)"; | |
} | |
} | |
/** | |
* Get the assembly of the SNPs. | |
* | |
* @return string The assembly of the SNPs | |
*/ | |
public function getAssembly(): string | |
{ | |
if ($this->_build === 37) { | |
return "GRCh37"; | |
} elseif ($this->_build === 36) { | |
return "NCBI36"; | |
} elseif ($this->_build === 38) { | |
return "GRCh38"; | |
} else { | |
return ""; | |
} | |
} | |
/** | |
* Assign PAR SNPs to the X or Y chromosome using SNP position. | |
* | |
* References: | |
* 1. National Center for Biotechnology Information, Variation Services, RefSNP, | |
* https://api.ncbi.nlm.nih.gov/variation/v0/ | |
* 2. Yates et. al. (doi:10.1093/bioinformatics/btu613), | |
* http://europepmc.org/search/?query=DOI:10.1093/bioinformatics/btu613 | |
* 3. Zerbino et. al. (doi.org/10.1093/nar/gkx1098), https://doi.org/10.1093/nar/gkx1098 | |
* 4. Sherry ST, Ward MH, Kholodov M, Baker J, Phan L, Smigielski EM, Sirotkin K. | |
* dbSNP: the NCBI database of genetic variation. Nucleic Acids Res. 2001 Jan 1; | |
* 29(1):308-11. | |
* 5. Database of Single Nucleotide Polymorphisms (dbSNP). Bethesda (MD): National Center | |
* for Biotechnology Information, National Library of Medicine. dbSNP accession: | |
* rs28736870, rs113313554, and rs758419898 (dbSNP Build ID: 151). Available from: | |
* http://www.ncbi.nlm.nih.gov/SNP/ | |
*/ | |
protected function assignParSnps() | |
{ | |
$restClient = $this->ensemblRestClient; | |
$snps = $this->filter(function ($snps) { | |
return $snps["chrom"] === "PAR"; | |
}); | |
foreach ($snps as $snp) { | |
$rsid = $snp["rsid"]; | |
echo "rsid: $rsid\n"; | |
if (str_starts_with($rsid, "rs")) { | |
$response = $this->lookupRefsnpSnapshot($rsid, $restClient); | |
// print_r($response); | |
if ($response !== null) { | |
// print_r($response["primary_snapshot_data"]["placements_with_allele"]); | |
foreach ($response["primary_snapshot_data"]["placements_with_allele"] as $item) { | |
// print_r($item["seq_id"]); | |
// var_dump(str_starts_with($item["seq_id"], "NC_000023")); | |
// var_dump(str_starts_with($item["seq_id"], "NC_000024")); | |
if (str_starts_with($item["seq_id"], "NC_000023")) { | |
$assigned = $this->assignSnp($rsid, $item["alleles"], "X"); | |
// var_dump($assigned); | |
} elseif (str_starts_with($item["seq_id"], "NC_000024")) { | |
$assigned = $this->assignSnp($rsid, $item["alleles"], "Y"); | |
// var_dump($assigned); | |
} else { | |
$assigned = false; | |
} | |
if ($assigned) { | |
if (!$this->_build_detected) { | |
$this->_build = $this->extractBuild($item); | |
$this->_build_detected = true; | |
} | |
break; | |
} | |
} | |
} | |
} | |
} | |
} | |
protected function extractBuild($item) | |
{ | |
$assembly_name = $item["placement_annot"]["seq_id_traits_by_assembly"][0]["assembly_name"]; | |
$assembly_name = explode(".", $assembly_name)[0]; | |
return intval(substr($assembly_name, -2)); | |
} | |
protected function assignSnp($rsid, $alleles, $chrom) | |
{ | |
// only assign SNP if positions match (i.e., same build) | |
foreach ($alleles as $allele) { | |
$allele_pos = $allele["allele"]["spdi"]["position"]; | |
// ref SNP positions seem to be 0-based... | |
// print_r($this->get($rsid)["pos"] - 1); | |
// echo "\n"; | |
// print_r($allele_pos); | |
if ($allele_pos == $this->get($rsid)["pos"] - 1) { | |
$this->setValue($rsid, "chrom", $chrom); | |
return true; | |
} | |
} | |
return false; | |
} | |
public function get($rsid) | |
{ | |
return $this->_snps[$rsid] ?? null; | |
} | |
public function setValue($rsid, $key, $value) | |
{ | |
echo "Setting {$rsid} {$key} to {$value}\n"; | |
$this->_snps[$rsid][$key] = $value; | |
} | |
private function lookupRefsnpSnapshot($rsid, $restClient) | |
{ | |
$id = str_replace("rs", "", $rsid); | |
$response = $restClient->perform_rest_action("/variation/v0/refsnp/" . $id); | |
if (isset($response["merged_snapshot_data"])) { | |
// this RefSnp id was merged into another | |
// we'll pick the first one to decide which chromosome this PAR will be assigned to | |
$mergedId = "rs" . $response["merged_snapshot_data"]["merged_into"][0]; | |
error_log("SNP id {$rsid} has been merged into id {$mergedId}"); // replace with your preferred logger | |
return $this->lookupRefsnpSnapshot($mergedId, $restClient); | |
} elseif (isset($response["nosnppos_snapshot_data"])) { | |
error_log("Unable to look up SNP id {$rsid}"); // replace with your preferred logger | |
return null; | |
} else { | |
return $response; | |
} | |
} | |
/** | |
* Sex derived from SNPs. | |
* | |
* @return string 'Male' or 'Female' if detected, else empty string | |
*/ | |
public function getSex() | |
{ | |
$sex = $this->determine_sex(chrom: "X"); | |
if (empty($sex)) | |
$sex = $this->determine_sex(chrom: "Y"); | |
return $sex; | |
} | |
/** | |
* Determine sex from SNPs using thresholds. | |
* | |
* @param float $heterozygous_x_snps_threshold percentage heterozygous X SNPs; above this threshold, Female is determined | |
* @param float $y_snps_not_null_threshold percentage Y SNPs that are not null; above this threshold, Male is determined | |
* @param string $chrom use X or Y chromosome SNPs to determine sex, default is "X" | |
* @return string 'Male' or 'Female' if detected, else empty string | |
*/ | |
public function determine_sex( | |
$heterozygous_x_snps_threshold = 0.03, | |
$y_snps_not_null_threshold = 0.3, | |
$chrom = "X" | |
) { | |
if (!empty($this->_snps)) { | |
if ($chrom === "X") { | |
return $this->_determine_sex_X($heterozygous_x_snps_threshold); | |
} elseif ($chrom === "Y") { | |
return $this->_determine_sex_Y($y_snps_not_null_threshold); | |
} | |
} | |
return ""; | |
} | |
public function _determine_sex_X($threshold) | |
{ | |
$x_snps = $this->get_count("X"); | |
if ($x_snps > 0) { | |
if (count($this->heterozygous("X")) / $x_snps > $threshold) { | |
return "Female"; | |
} else { | |
return "Male"; | |
} | |
} else { | |
return ""; | |
} | |
} | |
public function _determine_sex_Y($threshold) | |
{ | |
$y_snps = $this->get_count("Y"); | |
if ($y_snps > 0) { | |
if (count($this->notnull("Y")) / $y_snps > $threshold) { | |
return "Male"; | |
} else { | |
return "Female"; | |
} | |
} else { | |
return ""; | |
} | |
} | |
public function notnull($chrom = "") | |
{ | |
$df = $this->_filter($chrom); | |
$result = []; | |
foreach ($df as $rsid => $row) { | |
if ($row['genotype'] !== null) { | |
$result[$rsid] = $row; | |
} | |
} | |
return $result; | |
} | |
/** | |
* Get heterozygous SNPs. | |
* | |
* @param string $chrom (optional) chromosome (e.g., "1", "X", "MT") | |
* @return array normalized ``snps`` array | |
*/ | |
public function heterozygous($chrom = "") | |
{ | |
$df = $this->_filter($chrom); | |
$result = []; | |
foreach ($df as $rsid => $row) { | |
if ( | |
$row['genotype'] !== null | |
&& strlen($row['genotype']) == 2 | |
&& $row['genotype'][0] != $row['genotype'][1] | |
) { | |
$result[$rsid] = $row; | |
} | |
} | |
return $result; | |
} | |
/** | |
* Get homozygous SNPs. | |
* | |
* @param string $chrom (optional) chromosome (e.g., "1", "X", "MT") | |
* @return array normalized ``snps`` array | |
*/ | |
public function homozygous($chrom = "") | |
{ | |
$df = $this->_filter($chrom); | |
$result = []; | |
foreach ($df as $rsid => $row) { | |
if ( | |
$row['genotype'] !== null | |
&& strlen($row['genotype']) == 2 | |
&& $row['genotype'][0] == $row['genotype'][1] | |
) { | |
$result[$rsid] = $row; | |
} | |
} | |
return $result; | |
} | |
/* | |
* Determine if SNPs is valid. | |
* | |
* SNPs is valid when the input file has been successfully parsed. | |
* | |
* @return bool True if SNPs is valid | |
*/ | |
public function isValid(): bool | |
{ | |
if (empty($this->_snps)) { | |
return false; | |
} else { | |
return true; | |
} | |
} | |
/* | |
* Summary of SNPs. | |
* | |
* @return array Summary of SNPs | |
*/ | |
public function getSummary(): array | |
{ | |
if (!$this->isValid()) { | |
return []; | |
} else { | |
return [ | |
"source" => $this->source, | |
"assembly" => $this->getAssembly(), | |
"build" => $this->_build, | |
"build_detected" => $this->_build_detected, | |
"count" => $this->count, | |
"chromosomes" => $this->chromosomes_summary, | |
"sex" => $this->sex, | |
]; | |
} | |
} | |
public function getChromosomes() | |
{ | |
if (!empty($this->_snps)) { | |
$chromosomes = []; | |
foreach ($this->_snps as $snp) { | |
if (!in_array($snp["chrom"], $chromosomes)) | |
$chromosomes[] = $snp["chrom"]; | |
} | |
// var_dump($chromosomes); | |
// sort($chromosomes); | |
// var_dump($chromosomes); | |
return $chromosomes; | |
} else { | |
return []; | |
} | |
} | |
/** | |
* Summary of the chromosomes of SNPs. | |
* | |
* @return string human-readable listing of chromosomes (e.g., '1-3, MT'), empty string if no chromosomes | |
*/ | |
public function getChromosomesSummary() | |
{ | |
if (!empty($this->_snps)) { | |
$chroms = array_unique(array_column($this->_snps, "chrom")); | |
$int_chroms = array_filter($chroms, 'is_numeric'); | |
$str_chroms = array_filter($chroms, 'is_string'); | |
$as_range = function ($iterable) { | |
$l = array_values($iterable); | |
if (count($l) > 1) { | |
return "{$l[0]}-{$l[-1]}"; | |
} else { | |
return "{$l[0]}"; | |
} | |
}; | |
$int_chroms_strs = []; | |
$current_range = []; | |
for ($i = 0; $i < count($int_chroms); $i++) { | |
$current_range[] = $int_chroms[$i]; | |
if ($i == count($int_chroms) - 1 || $int_chroms[$i] + 1 != $int_chroms[$i + 1]) { | |
$int_chroms_strs[] = $as_range($current_range); | |
$current_range = []; | |
} | |
} | |
$int_chroms = implode(", ", $int_chroms_strs); | |
$str_chroms = implode(", ", $str_chroms); | |
if ($int_chroms != "" && $str_chroms != "") { | |
$int_chroms .= ", "; | |
} | |
return $int_chroms . $str_chroms; | |
} else { | |
return ""; | |
} | |
} | |
/** | |
* Get PAR regions for the X and Y chromosomes. | |
* | |
* @param int $build Build of SNPs | |
* | |
* @return array PAR regions for the given build | |
* | |
* References: | |
* 1. Genome Reference Consortium, https://www.ncbi.nlm.nih.gov/grc/human | |
* 2. Yates et. al. (doi:10.1093/bioinformatics/btu613), | |
* <http://europepmc.org/search/?query=DOI:10.1093/bioinformatics/btu613> | |
* 3. Zerbino et. al. (doi.org/10.1093/nar/gkx1098), https://doi.org/10.1093/nar/gkx1098 | |
*/ | |
public static function getParRegions($build) | |
{ | |
if ($build == 37) { | |
return [ | |
"region" => ["PAR1", "PAR2", "PAR1", "PAR2"], | |
"chrom" => ["X", "X", "Y", "Y"], | |
"start" => [60001, 154931044, 10001, 59034050], | |
"stop" => [2699520, 155260560, 2649520, 59363566], | |
]; | |
} elseif ($build == 38) { | |
return [ | |
"region" => ["PAR1", "PAR2", "PAR1", "PAR2"], | |
"chrom" => ["X", "X", "Y", "Y"], | |
"start" => [10001, 155701383, 10001, 56887903], | |
"stop" => [2781479, 156030895, 2781479, 57217415], | |
]; | |
} elseif ($build == 36) { | |
return [ | |
"region" => ["PAR1", "PAR2", "PAR1", "PAR2"], | |
"chrom" => ["X", "X", "Y", "Y"], | |
"start" => [1, 154584238, 1, 57443438], | |
"stop" => [2709520, 154913754, 2709520, 57772954], | |
]; | |
} else { | |
return []; | |
} | |
} | |
private function getNonParStartStop($chrom) | |
{ | |
// Get non-PAR start / stop positions for chrom | |
$pr = $this->getParRegions($this->build); | |
$np_start = $np_stop = 0; | |
foreach ($pr as $row) { | |
if ($row['chrom'] == $chrom && $row['region'] == 'PAR1') { | |
$np_start = $row['stop']; | |
} | |
if ($row['chrom'] == $chrom && $row['region'] == 'PAR2') { | |
$np_stop = $row['start']; | |
} | |
} | |
return [$np_start, $np_stop]; | |
} | |
private function _get_non_par_snps($chrom, $heterozygous = true) | |
{ | |
list($np_start, $np_stop) = $this->getNonParStartStop($chrom); | |
$df = $this->_filter($chrom); | |
$result = []; | |
foreach ($df as $index => $row) { | |
if ($row['genotype'] !== null && strlen($row['genotype']) == 2) { | |
$genotype0 = $row['genotype'][0]; | |
$genotype1 = $row['genotype'][1]; | |
if ($heterozygous && $genotype0 != $genotype1 && $row['pos'] > $np_start && $row['pos'] < $np_stop) { | |
$result[] = $index; | |
} elseif (!$heterozygous && $genotype0 == $genotype1 && $row['pos'] > $np_start && $row['pos'] < $np_stop) { | |
$result[] = $index; | |
} | |
} | |
} | |
return $result; | |
} | |
private function _deduplicate_rsids() | |
{ | |
// Keep first duplicate rsid. | |
print_r($this->_snps); | |
$rsids = array_column($this->_snps, 'rsid'); | |
$duplicateRsids = array_filter( | |
$this->_snps, | |
function ($value, $key) use ($rsids) { | |
$keys = array_keys($rsids, $value['rsid']); | |
return count($keys) > 1 && in_array($key, $keys); | |
}, | |
ARRAY_FILTER_USE_BOTH | |
); | |
// Save duplicate SNPs | |
$this->_duplicate = array_merge($this->_duplicate, $duplicateRsids); | |
// Deduplicate | |
echo "\nrrrrrrrrrrrrrrrrr\n"; | |
print_r($duplicateRsids); | |
$this->setSNPs(array_diff_key($this->_snps, $duplicateRsids)); | |
echo "\nddddddddddddddddddd\n"; | |
print_r($this->_snps); | |
} | |
private function _deduplicate_alleles($rsids) | |
{ | |
// Remove duplicate allele | |
foreach ($rsids as $rsid) { | |
if (isset($this->_snps[$rsid])) { | |
$this->_snps[$rsid]['genotype'] = $this->_snps[$rsid]['genotype'][0]; | |
} | |
} | |
} | |
private function _deduplicate_sex_chrom($chrom) | |
{ | |
$discrepantXYSnps = $this->_get_non_par_snps($chrom); | |
// Save discrepant XY SNPs | |
foreach ($discrepantXYSnps as $snps) { | |
$this->_discrepant_XY[] = $this->_snps[$snps]; | |
} | |
// Drop discrepant XY SNPs since it's ambiguous for which allele to deduplicate | |
foreach ($discrepantXYSnps as $snps) { | |
unset($this->_snps[$snps]); | |
} | |
// Get remaining non-PAR SNPs with two alleles | |
$nonParSnps = $this->_get_non_par_snps($chrom, false); | |
// echo "nonParSnps\n"; | |
// var_dump($nonParSnps); | |
$this->_deduplicate_alleles($nonParSnps); | |
} | |
public function deduplicate_XY_chrom() | |
{ | |
$this->_deduplicate_sex_chrom("X"); | |
$this->_deduplicate_sex_chrom("Y"); | |
} | |
private function deduplicate_MT_chrom() | |
{ | |
$heterozygousMTSnps = $this->heterozygous("MT"); | |
// Save heterozygous MT SNPs | |
foreach ($heterozygousMTSnps as $snps) { | |
$this->_heterozygous_MT[] = $snps; | |
} | |
// Drop heterozygous MT SNPs since it's ambiguous for which allele to deduplicate | |
foreach ($heterozygousMTSnps as $snps) { | |
unset($this->_snps[$snps["rsid"]]); | |
} | |
$this->_deduplicate_alleles(array_column($this->homozygous("MT"), "rsid")); | |
} | |
public function sort() | |
{ | |
$sortedList = $this->naturalSortChromosomes(array_unique(array_column($this->_snps, 'chrom'))); | |
// Move PAR and MT to the end of the array | |
if (($key = array_search("PAR", $sortedList)) !== false) { | |
unset($sortedList[$key]); | |
$sortedList[] = "PAR"; | |
} | |
if (($key = array_search("MT", $sortedList)) !== false) { | |
unset($sortedList[$key]); | |
$sortedList[] = "MT"; | |
} | |
uasort($this->_snps, function ($a, $b) use ($sortedList) { | |
$cmp = $this->naturalSortKey( | |
array_search($a['chrom'], $sortedList), | |
array_search($b['chrom'], $sortedList) | |
); | |
return ($cmp === 0) ? $a['pos'] - $b['pos'] : $cmp; | |
}); | |
$this->setSNPs($this->restoreChromObject($this->_snps)); | |
} | |
private function naturalSortChromosomes($chromosomes) | |
{ | |
natsort($chromosomes); | |
return $chromosomes; | |
} | |
private function naturalSortKey($a, $b) | |
{ | |
return strnatcasecmp($a, $b); | |
} | |
private function restoreChromObject($array) | |
{ | |
// Convert the 'chrom' column back to object | |
foreach ($array as &$item) { | |
$item['chrom'] = (string)$item['chrom']; | |
} | |
return $array; | |
} | |
private function _complement_bases($genotype) | |
{ | |
if (is_null($genotype)) { | |
return null; | |
} | |
$complement = ""; | |
foreach (str_split($genotype) as $base) { | |
if ($base === "A") { | |
$complement .= "T"; | |
} elseif ($base === "G") { | |
$complement .= "C"; | |
} elseif ($base === "C") { | |
$complement .= "G"; | |
} elseif ($base === "T") { | |
$complement .= "A"; | |
} else { | |
$complement .= $base; | |
} | |
} | |
return $complement; | |
} | |
private function _remapper($task) | |
{ | |
$temp = $task['snps']->copy(); | |
$mappings = $task['mappings']; | |
$complement_bases = $task['complement_bases']; | |
$temp['remapped'] = false; | |
$pos_start = (int)$temp['pos']->describe()->min; | |
$pos_end = (int)$temp['pos']->describe()->max; | |
foreach ($mappings['mappings'] as $mapping) { | |
$orig_start = $mapping['original']['start']; | |
$orig_end = $mapping['original']['end']; | |
$mapped_start = $mapping['mapped']['start']; | |
$mapped_end = $mapping['mapped']['end']; | |
$orig_region = $mapping['original']['seq_region_name']; | |
$mapped_region = $mapping['mapped']['seq_region_name']; | |
// skip if mapping is outside of range of SNP positions | |
if ($orig_end < $pos_start || $orig_start > $pos_end) { | |
continue; | |
} | |
// find the SNPs that are being remapped for this mapping | |
$snp_indices = array_keys(array_filter( | |
$temp['remapped'], | |
function ($value, $key) use ($temp, $orig_start, $orig_end) { | |
return !$value && $temp['pos'][$key] >= $orig_start && $temp['pos'][$key] <= $orig_end; | |
}, | |
ARRAY_FILTER_USE_BOTH | |
)); | |
// if there are no SNPs here, skip | |
if (count($snp_indices) === 0) { | |
continue; | |
} | |
$orig_range_len = $orig_end - $orig_start; | |
$mapped_range_len = $mapped_end - $mapped_start; | |
// if this would change chromosome, skip | |
// TODO allow within normal chromosomes | |
// TODO flatten patches | |
if ($orig_region != $mapped_region) { | |
// Logger::warning( | |
// "discrepant chroms for " . count($snp_indices) . " SNPs from $orig_region to $mapped_region" | |
// ); | |
continue; | |
} | |
// if there is any stretching or squashing of the region | |
// observed when mapping NCBI36 -> GRCh38 | |
// TODO disallow skipping a version when remapping | |
if ($orig_range_len != $mapped_range_len) { | |
// Logger::warning( | |
// "discrepant coords for " . count($snp_indices) . " SNPs from $orig_region:$orig_start-$orig_end to $mapped_region:$mapped_start-$mapped_end" | |
// ); | |
continue; | |
} | |
// remap the SNPs | |
if ($mapping['mapped']['strand'] == -1) { | |
// flip and (optionally) complement since we're mapping to minus strand | |
$diff_from_start = array_map( | |
function ($pos) use ($orig_start) { | |
return $pos - $orig_start; | |
}, | |
array_intersect_key($temp['pos'], array_flip($snp_indices)) | |
); | |
$temp['pos'][array_flip($snp_indices)] = array_map( | |
function ($diff, $mapped_end) { | |
return $mapped_end - $diff; | |
}, | |
$diff_from_start, | |
array_fill(0, count($snp_indices), $mapped_end) | |
); | |
if ($complement_bases) { | |
$temp['genotype'][array_flip($snp_indices)] = array_map( | |
[$this, '_complement_bases'], | |
array_intersect_key($temp['genotype'], array_flip($snp_indices)) | |
); | |
} | |
} else { | |
// mapping is on the same (plus) strand, so just remap based on offset | |
$offset = $mapped_start - $orig_start; | |
$temp['pos'][array_flip($snp_indices)] = array_map( | |
function ($pos) use ($offset) { | |
return $pos + $offset; | |
}, | |
array_intersect_key($temp['pos'], array_flip($snp_indices)) | |
); | |
} | |
// mark these SNPs as remapped | |
$temp['remapped'][array_flip($snp_indices)] = array_fill(0, count($snp_indices), true); | |
} | |
return $temp; | |
} | |
/** | |
* Remap SNP coordinates from one assembly to another. | |
* | |
* This method uses the assembly map endpoint of the Ensembl REST API service (via | |
* Resources's EnsemblRestClient) to convert SNP coordinates/positions from one | |
* assembly to another. After remapping, the coordinates/positions for the | |
* SNPs will be that of the target assembly. | |
* | |
* If the SNPs are already mapped relative to the target assembly, remapping will not be | |
* performed. | |
* | |
* @param string|int $target_assembly Assembly to remap to (e.g., 'NCBI36', 'GRCh37', 'GRCh38', 36, 37, 38) | |
* @param bool $complement_bases Complement bases when remapping SNPs to the minus strand | |
* | |
* @return array An array containing chromosomes that were remapped and chromosomes that were not remapped | |
* | |
* @throws Exception If invalid target assembly is provided | |
*/ | |
public function remap($target_assembly, $complement_bases = true) | |
{ | |
$chromosomes_remapped = []; | |
$chromosomes_not_remapped = []; | |
$snps = $this->_snps; | |
if (empty($snps)) { | |
// Logger::warning("No SNPs to remap"); | |
return [$chromosomes_remapped, $chromosomes_not_remapped]; | |
} else { | |
$chromosomes = array_unique(array_column($snps, 'chrom')); | |
$chromosomes_not_remapped = $chromosomes; | |
} | |
$valid_assemblies = ["NCBI36", "GRCh37", "GRCh38", 36, 37, 38]; | |
if (!in_array($target_assembly, $valid_assemblies)) { | |
// Logger::warning("Invalid target assembly"); | |
return [$chromosomes_remapped, $chromosomes_not_remapped]; | |
} | |
if (is_int($target_assembly)) { | |
if ($target_assembly == 36) { | |
$target_assembly = "NCBI36"; | |
} else { | |
$target_assembly = "GRCh" . strval($target_assembly); | |
} | |
} | |
if ($this->_build == 36) { | |
$source_assembly = "NCBI36"; | |
} else { | |
$source_assembly = "GRCh" . strval($this->_build); | |
} | |
if ($source_assembly == $target_assembly) { | |
return [$chromosomes_remapped, $chromosomes_not_remapped]; | |
} | |
$assembly_mapping_data = $this->_resources->getAssemblyMappingData( | |
$source_assembly, | |
$target_assembly | |
); | |
if (empty($assembly_mapping_data)) { | |
return [$chromosomes_remapped, $chromosomes_not_remapped]; | |
} | |
$tasks = []; | |
foreach ($chromosomes as $chrom) { | |
if (array_key_exists($chrom, $assembly_mapping_data)) { | |
$chromosomes_remapped[] = $chrom; | |
$chromosomes_not_remapped = array_diff($chromosomes_not_remapped, [$chrom]); | |
$mappings = $assembly_mapping_data[$chrom]; | |
$tasks[] = [ | |
"snps" => array_filter($snps, function ($snp) use ($chrom) { | |
return $snp['chrom'] === $chrom; | |
}), | |
"mappings" => $mappings, | |
"complement_bases" => $complement_bases, | |
]; | |
} else { | |
// Logger::warning( | |
// "Chromosome $chrom not remapped; removing chromosome from SNPs for consistency" | |
// ); | |
$snps = array_filter($snps, function ($snp) use ($chrom) { | |
return $snp['chrom'] !== $chrom; | |
}); | |
} | |
} | |
// remap SNPs | |
$remapped_snps = array_map([$this, '_remapper'], $tasks); | |
$remapped_snps = array_merge(...$remapped_snps); | |
// update SNP positions and genotypes | |
foreach ($remapped_snps as $snp) { | |
$rsid = $snp['rsid']; | |
$this->_snps[$rsid]['pos'] = $snp['pos']; | |
$this->_snps[$rsid]['genotype'] = $snp['genotype']; | |
} | |
foreach ($snps as &$snp) { | |
$snp['pos'] = (int)$snp['pos']; | |
} | |
$this->setSNPs($snps); | |
$this->sort(); | |
$this->_build = (int)substr($target_assembly, -2); | |
return [$chromosomes_remapped, $chromosomes_not_remapped]; | |
} | |
/** | |
* Save SNPs to a file. | |
* | |
* @param string $filename | |
* @param bool $vcf | |
* @param bool $atomic | |
* @param string $vcf_alt_unavailable | |
* @param string $vcf_chrom_prefix | |
* @param bool $vcf_qc_only | |
* @param bool $vcf_qc_filter | |
* @param array $kwargs | |
* | |
* @return string | |
*/ | |
public function save( | |
$filename = "", | |
$vcf = false, | |
$atomic = true, | |
$vcf_alt_unavailable = ".", | |
$vcf_chrom_prefix = "", | |
$vcf_qc_only = false, | |
$vcf_qc_filter = false, | |
$kwargs = [] | |
) { | |
if (!array_key_exists("sep", $kwargs)) { | |
$kwargs["sep"] = "\t"; | |
} | |
$w = new Writer( | |
[ | |
'snps' => $this, | |
'filename' => $filename, | |
'vcf' => $vcf, | |
'atomic' => $atomic, | |
'vcf_alt_unavailable' => $vcf_alt_unavailable, | |
'vcf_chrom_prefix' => $vcf_chrom_prefix, | |
'vcf_qc_only' => $vcf_qc_only, | |
'vcf_qc_filter' => $vcf_qc_filter | |
], | |
$kwargs | |
); | |
$result = $w->write(); | |
[$path, $extra] = $result; | |
if (count($extra) == 1 && !$extra[0]->isEmpty()) { | |
$this->_discrepant_vcf_position = $extra[0]; | |
$this->_discrepant_vcf_position->setIndex("rsid"); | |
// logger::warning( | |
// count($this->discrepant_vcf_position) . " SNP positions were found to be discrepant when saving VCF" | |
// ); | |
} | |
return $path; | |
} | |
/** | |
* Output SNPs as comma-separated values. | |
* | |
* @param string $filename | |
* @param bool $atomic | |
* @param array $kwargs | |
* | |
* @return string | |
*/ | |
public function toCsv($filename = "", $atomic = true, $kwargs = []) | |
{ | |
$kwargs["delimiter"] = ","; | |
return $this->save($filename, $atomic, $kwargs); | |
} | |
/** | |
* Output SNPs as tab-separated values. | |
* | |
* @param string $filename | |
* @param bool $atomic | |
* @param array $kwargs | |
* | |
* @return string | |
*/ | |
public function toTsv($filename = "", $atomic = true, $kwargs = []) | |
{ | |
$kwargs["delimiter"] = "\t"; | |
return $this->save($filename, $atomic, $kwargs); | |
} | |
/** | |
* Output SNPs as Variant Call Format (VCF). | |
* | |
* @param string $filename | |
* @param bool $atomic | |
* @param string $alt_unavailable | |
* @param string $chrom_prefix | |
* @param bool $qc_only | |
* @param bool $qc_filter | |
* @param array $kwargs | |
* | |
* @return string | |
*/ | |
public function toVcf( | |
$filename = "", | |
$atomic = true, | |
$alt_unavailable = ".", | |
$chrom_prefix = "", | |
$qc_only = false, | |
$qc_filter = false, | |
$kwargs = [] | |
) { | |
return $this->save( | |
$filename, | |
true, | |
[ | |
'vcf' => true, | |
'atomic' => $atomic, | |
'vcf_alt_unavailable' => $alt_unavailable, | |
'vcf_chrom_prefix' => $chrom_prefix, | |
'vcf_qc_only' => $qc_only, | |
'vcf_qc_filter' => $qc_filter, | |
] + $kwargs | |
); | |
} | |
} | |
// protected function initSnps() { | |
// if ($this->file) { | |
// $d = $this->_read_raw_data($file, $only_detect_source, $rsids); | |
// // Replace multiple rsids separated by commas in index with the first rsid. E.g. rs1,rs2 -> rs1 | |
// $multi_rsids = []; | |
// foreach ($d["snps"] as $multi_rsid) { | |
// if (count(explode(",", $multi_rsid)) > 1) { | |
// $multi_rsids[$multi_rsid] = explode(",", $multi_rsid)[0]; | |
// } | |
// } | |
// $d["snps"] = array_replace_key($d["snps"], $_rsids); | |
// $this->_snps = $d["snps"]; | |
// $this->_source = (strpos($d["source"], ", ") !== false) ? explode(", ", $d["source"]) : [$d["source"]]; | |
// $this->_phased = $d["phased"]; | |
// $this->_build = $d["build"]; | |
// $this->_build_detected = $d["build"] ? true : false; | |
// if (!empty($this->_snps)) { | |
// $this->sort(); | |
// if ($deduplicate) { | |
// $this->_deduplicate_rsids(); | |
// } | |
// // use build detected from `read` method or comments, if any | |
// // otherwise use SNP positions to detect build | |
// if (!$this->_build_detected) { | |
// $this->_build = $this->detect_build(); | |
// $this->_build_detected = $this->_build ? true : false; | |
// if (!$this->_build) { | |
// $this->_build = 37; // assume Build 37 / GRCh37 if not detected | |
// } else { | |
// $this->_build_detected = true; | |
// } | |
// } | |
// if ($assign_par_snps) { | |
// $this->_assign_par_snps(); | |
// $this->sort(); | |
// } | |
// if ($deduplicate_XY_chrom) { | |
// if ($deduplicate_XY_chrom === true && $this->determine_sex() === "Male" || $this->determine_sex(chrom: $deduplicate_XY_chrom) === "Male") { | |
// $this->_deduplicate_XY_chrom(); | |
// } | |
// } | |
// if ($deduplicate_MT_chrom) { | |
// $this->_deduplicate_MT_chrom(); | |
// } | |
// } else { | |
// // Use PHP's error_log function or other logging library to display a warning | |
// error_log("no SNPs loaded..."); | |
// } | |
// } | |
// } | |
// /** | |
// * Get the default CPU count. | |
// * | |
// * @return int Default CPU count | |
// */ | |
// private static function default_cpu_count(): int | |
// { | |
// return sys_get_temp_dir(); | |
// } | |
// /** | |
// * Get the length of the SNPs. | |
// * | |
// * @return int The count of SNPs | |
// */ | |
// public function length() | |
// { | |
// return $this->count; | |
// } | |
// | |
// /** | |
// * Identify low quality SNPs. | |
// * | |
// * @return void | |
// */ | |
// public function identify_low_quality_snps(): void | |
// { | |
// // Implement the method in PHP. | |
// } | |
// /** | |
// * Get the SNPs after quality control filtering. | |
// * | |
// * @return array The SNPs array after quality control filtering | |
// */ | |
// public function getSnpsQc(): array | |
// { | |
// if (count($this->_low_quality) == 0) { | |
// // Ensure low quality SNPs, if any, are identified | |
// $this->identify_low_quality_snps(); | |
// } | |
// if (count($this->_low_quality) > 0) { | |
// // Filter out low quality SNPs | |
// return array_diff_key($this->_snps, array_flip($this->_low_quality)); | |
// } else { | |
// // No low quality SNPs to filter | |
// return $this->_snps; | |
// } | |
// } | |
// /** | |
// * Get the duplicate SNPs. | |
// * | |
// * @return array The duplicate SNPs array | |
// */ | |
// public function getDuplicate(): array | |
// { | |
// return $this->_duplicate; | |
// } | |
// public function getDiscrepantXY(): array | |
// { | |
// // Discrepant XY SNPs. | |
// // | |
// // A discrepant XY SNP is a heterozygous SNP in the non-PAR region of the X | |
// // or Y chromosome found during deduplication for a detected male genotype. | |
// // | |
// // Returns | |
// // ------- | |
// // array | |
// // normalized "snps" array | |
// return $this->_discrepantXY; | |
// } | |
// public function getHeterozygousMT(): array | |
// { | |
// // Heterozygous SNPs on the MT chromosome found during deduplication. | |
// // | |
// // Returns | |
// // ------- | |
// // array | |
// // normalized "snps" array | |
// return $this->_heterozygousMT; | |
// } | |
// public function getDiscrepantVcfPosition(): array | |
// { | |
// // SNPs with discrepant positions discovered while saving VCF. | |
// // | |
// // Returns | |
// // ------- | |
// // array | |
// // normalized "snps" array | |
// return $this->_discrepantVcfPosition; | |
// } | |
// private array $lowQuality = []; | |
// private DataFrame $_snps; | |
// private DataFrame $_discrepant_merge_positions; | |
// public function getLowQuality(): DataFrame | |
// { | |
// if (count($this->lowQuality) === 0) { | |
// // Ensure low quality SNPs, if any, are identified | |
// $this->identify_low_quality_snps(); | |
// } | |
// return $this->_snps->loc($this->lowQuality); | |
// } | |
// public function getDiscrepantMergePositions(): DataFrame | |
// { | |
// // Get the DataFrame of SNPs with discrepant merge positions. | |
// // | |
// // Returns | |
// // ------- | |
// // DataFrame | |
// // DataFrame containing SNPs with discrepant merge positions | |
// return $this->_discrepant_merge_positions; | |
// } | |
// public function getDiscrepantMergeGenotypes(): DataFrame { | |
// // Get the DataFrame of SNPs with discrepant merge genotypes. | |
// // | |
// // Returns | |
// // ------- | |
// // DataFrame | |
// // DataFrame containing SNPs with discrepant merge genotypes | |
// return $this->_discrepant_merge_genotypes; | |
// } | |
// public function getDiscrepantMergePositionsGenotypes(): DataFrame { | |
// // Get the DataFrame of SNPs with discrepant merge positions and genotypes. | |
// // | |
// // Returns | |
// // ------- | |
// // DataFrame | |
// // DataFrame containing SNPs with discrepant merge positions and genotypes | |
// $df = DataFrame::concat([$this->_discrepant_merge_positions, $this->_discrepant_merge_genotypes]); | |
// if (count($df) > 1) { | |
// $df = DataFrame::dropDuplicates($df); | |
// } | |
// return $df; | |
// } | |
// public function getBuild(): int | |
// { | |
// // Get the build number associated with the data. | |
// // | |
// // Returns | |
// // ------- | |
// // int | |
// // The build number | |
// return $this->_build; | |
// } | |
// | |
// | |
// | |
// | |
// public function getChromosomesSummary(): string | |
// { | |
// // Check if the "_snps" array is not empty | |
// if (!$this->_snps->isEmpty()) { | |
// // Get unique values of the "chrom" key in the "_snps" array | |
// $chroms = array_unique($this->_snps["chrom"]); | |
// // Separate integer and non-integer chromosomes | |
// $intChroms = array_filter($chroms, fn($chrom) => ctype_digit($chrom)); | |
// $strChroms = array_filter($chroms, fn($chrom) => !ctype_digit($chrom)); | |
// // Initialize an array to store ranges of integer chromosomes | |
// $intRanges = []; | |
// $start = null; | |
// $prev = null; | |
// // Sort the integer chromosomes in ascending order | |
// sort($intChroms); | |
// // Iterate over the sorted integer chromosomes | |
// foreach ($intChroms as $current) { | |
// if ($start === null) { | |
// // Set the start of a new range | |
// $start = $current; | |
// } else if ($prev !== null && $current !== $prev + 1) { | |
// // If the current number is not consecutive to the previous one, | |
// // add the range to the array | |
// $intRanges[] = ($start === $prev) ? $start : "{$start}-{$prev}"; | |
// $start = $current; | |
// } | |
// $prev = $current; | |
// } | |
// // Add the last range (if any) to the array | |
// if ($start !== null) { | |
// $intRanges[] = ($start === $prev) ? $start : "{$start}-{$prev}"; | |
// } | |
// // Convert the ranges and non-integer chromosomes to strings | |
// $intChromsStr = implode(", ", $intRanges); | |
// $strChromsStr = implode(", ", $strChroms); | |
// if ($intChromsStr !== "" && $strChromsStr !== "") { | |
// $intChromsStr .= ", "; | |
// } | |
// // Return the concatenated string representation of the chromosomes | |
// return $intChromsStr . $strChromsStr; | |
// } else { | |
// // Return an empty string if "_snps" is empty | |
// return ""; | |
// } | |
// } | |
// public function getSex(): string | |
// { | |
// // Determine sex based on the presence of specific chromosomes | |
// $sex = $this->determineSex(chrom: "X"); | |
// if (!$sex) { | |
// $sex = $this->determineSex(chrom: "Y"); | |
// } | |
// // Return the determined sex | |
// return $sex; | |
// } | |
// public function filter(string $chrom = ''): array | |
// { | |
// // Implement the filtering logic here | |
// // Add your implementation code and comments here | |
// return []; | |
// } | |
// public function save( | |
// string $filename = "", | |
// bool $vcf = false, | |
// bool $atomic = true, | |
// string $vcf_alt_unavailable = ".", | |
// bool $vcf_qc_only = false, | |
// bool $vcf_qc_filter = false, | |
// array $kwargs = [] // For compatibility, changed **kwargs to array | |
// ): bool | |
// { | |
// // Trigger a deprecated error indicating that 'save' method should be replaced | |
// trigger_error( | |
// "Method 'save' has been replaced by 'to_csv', 'to_tsv', and 'to_vcf'.", | |
// E_USER_DEPRECATED | |
// ); | |
// // Call the internal '_save' method with the provided arguments | |
// return $this->_save( | |
// $filename, | |
// $vcf, | |
// $atomic, | |
// $vcf_alt_unavailable, | |
// $vcf_qc_only, | |
// $vcf_qc_filter, | |
// $kwargs | |
// ); | |
// } | |
// public function toCsv(string $filename = "", bool $atomic = true, array $options = []): string | |
// { | |
// // Set the separator to comma (",") in the options array | |
// $options["sep"] = ","; | |
// // Call the 'save' method with the provided arguments and return the result | |
// return $this->save($filename, $atomic, $options); | |
// } | |
// public function toTsv(string $filename = "", bool $atomic = true, array $options = []): string | |
// { | |
// // Set the separator to tab ("\t") in the options array | |
// $options["sep"] = "\t"; | |
// // Call the 'save' method with the provided arguments and return the result | |
// return $this->save($filename, $atomic, $options); | |
// } | |
// public function to_vcf( | |
// string $filename = "", | |
// bool $atomic = true, | |
// string $alt_unavailable = ".", | |
// string $chrom_prefix = "", | |
// bool $qc_only = false, | |
// bool $qc_filter = false, | |
// array $kwargs = [] | |
// ): string { | |
// // Output SNPs as Variant Call Format. | |
// // | |
// // Parameters: | |
// // $filename : str or buffer | |
// // filename for file to save or buffer to write to | |
// // $atomic : bool | |
// // atomically write output to a file on the local filesystem | |
// // $alt_unavailable : str | |
// // representation of ALT allele when ALT is not able to be determined | |
// // $chrom_prefix : str | |
// // prefix for chromosomes in VCF CHROM column | |
// // $qc_only : bool | |
// // output only SNPs that pass quality control | |
// // $qc_filter : bool | |
// // populate FILTER column based on quality control results | |
// // $kwargs : array | |
// // additional parameters to pandas.DataFrame.to_csv | |
// // | |
// // Returns: | |
// // str | |
// // path to file in output directory if SNPs were saved, else empty str | |
// // | |
// // Notes: | |
// // Parameters $qc_only and $qc_filter, if true, will identify low-quality SNPs per | |
// // "identify_low_quality_snps" method in the SNPs class if not done already. | |
// // Moreover, these parameters have no effect if this SNPs object does not map to a cluster | |
// // per "compute_cluster_overlap" method in the SNPs class. | |
// // | |
// // References: | |
// // 1. The Variant Call Format (VCF) Version 4.2 Specification, 8 Mar 2019, | |
// // https://samtools.github.io/hts-specs/VCFv4.2.pdf | |
// return $this->_save( | |
// filename: $filename, | |
// vcf: true, | |
// atomic: $atomic, | |
// vcf_alt_unavailable: $alt_unavailable, | |
// vcf_chrom_prefix: $chrom_prefix, | |
// vcf_qc_only: $qc_only, | |
// vcf_qc_filter: $qc_filter, | |
// kwargs: $kwargs | |
// ); | |
// } | |
// public function read_raw_data($file, $only_detect_source, $rsids) { | |
// // Create a new instance of the Reader class | |
// $reader = new Reader($file, $only_detect_source, $this->resources, $rsids); | |
// // Read and return the data using the Reader instance | |
// return $reader->read(); | |
// } | |
// public function assign_par_snps() { | |
// // Create a new instance of the EnsemblRestClient class with configuration options | |
// $rest_client = new EnsemblRestClient([ | |
// 'server' => 'https://api.ncbi.nlm.nih.gov', | |
// 'reqs_per_sec' => 1, | |
// ]); | |
// // Iterate over the snps collection to find 'PAR' keys | |
// foreach ($this->snps->where('chrom', 'PAR')->keys() as $rsid) { | |
// if (strpos($rsid, 'rs') !== false) { | |
// // Lookup the refsnp snapshot using the EnsemblRestClient | |
// $response = $this->lookup_refsnp_snapshot($rsid, $rest_client); | |
// if ($response !== null) { | |
// // Iterate over the placements_with_allele in the response | |
// foreach ($response['primary_snapshot_data']['placements_with_allele'] as $item) { | |
// if (strpos($item['seq_id'], 'NC_000023') !== false) { | |
// // Assign the snp with 'X' chromosome if seq_id contains 'NC_000023' | |
// $assigned = $this->assign_snp($rsid, $item['alleles'], 'X'); | |
// } elseif (strpos($item['seq_id'], 'NC_000024') !== false) { | |
// // Assign the snp with 'Y' chromosome if seq_id contains 'NC_000024' | |
// $assigned = $this->assign_snp($rsid, $item['alleles'], 'Y'); | |
// } else { | |
// $assigned = false; | |
// } | |
// if ($assigned) { | |
// // Update the build if not already detected and break the loop | |
// if (!$this->build_detected) { | |
// $this->build = $this->extract_build($item); | |
// $this->build_detected = true; | |
// } | |
// break; | |
// } | |
// } | |
// } | |
// } | |
// } | |
// } | |
// | |
// private function assignSnp(string $rsid, array $alleles, string $chrom) | |
// { | |
// // Only assign the SNP if positions match (i.e., same build) | |
// foreach ($alleles as $allele) { | |
// $allele_pos = $allele["allele"]["spdi"]["position"]; | |
// // Ref SNP positions seem to be 0-based... | |
// if ($allele_pos == ($this->snps[$rsid]->pos - 1)) { | |
// $this->snps[$rsid]->chrom = $chrom; | |
// return true; | |
// } | |
// } | |
// return false; | |
// } | |
// private function extractBuild(array $item) | |
// { | |
// $assembly_name = $item["placement_annot"]["seq_id_traits_by_assembly"][0]["assembly_name"]; | |
// // Extract the assembly name from the item | |
// $assembly_name = explode(".", $assembly_name)[0]; | |
// // Extract the build number from the assembly name | |
// return (int)substr($assembly_name, -2); | |
// } | |
// public function detectBuild(): int | |
// { | |
// // Define a closure to look up the build based on SNP position | |
// $lookupBuildWithSnpPos = function ($pos, $s) { | |
// // Search for the position in the sorted collection and retrieve the corresponding value (build) | |
// return isset($s[$s->search($pos)]) ? (int)$s->keys()[$s->search($pos)] : 0; | |
// }; | |
// $build = 0; | |
// // List of rsids to detect the build | |
// $rsids = [ | |
// "rs3094315", | |
// "rs11928389", | |
// "rs2500347", | |
// "rs964481", | |
// "rs2341354", | |
// "rs3850290", | |
// "rs1329546", | |
// ]; | |
// // Data frame with build positions for the rsids | |
// $df = [ | |
// 36 => [ | |
// 742429, | |
// 50908372, | |
// 143649677, | |
// 27566744, | |
// 908436, | |
// 22315141, | |
// 135302086, | |
// ], | |
// 37 => [ | |
// 752566, | |
// 50927009, | |
// 144938320, | |
// 27656823, | |
// 918573, | |
// 23245301, | |
// 135474420, | |
// ], | |
// 38 => [ | |
// 817186, | |
// 50889578, | |
// 148946169, | |
// 27638706, | |
// 983193, | |
// 22776092, | |
// 136392261, | |
// ], | |
// ]; | |
// foreach ($rsids as $rsid) { | |
// if (array_key_exists($rsid, $this->_snps)) { | |
// // Create a collection from the data frame and map the values to SNP positions | |
// $s = collect($df)->mapWithKeys(function ($value, $key) use ($rsid) { | |
// return [$value[array_search($rsid, $rsids)] => $key]; | |
// }); | |
// // Look up the build based on the SNP position and the mapped collection | |
// $build = $lookupBuildWithSnpPos($this->_snps[$rsid]['pos'], $s); | |
// } | |
// if ($build) { | |
// // If the build is detected, break the loop | |
// break; | |
// } | |
// } | |
// return $build; | |
// } | |
// /** | |
// * Determine the sex based on X chromosome SNPs. | |
// * | |
// * @param float $threshold The threshold for determining sex. | |
// * @return string The determined sex ("Male", "Female") or an empty string if unknown. | |
// */ | |
// public function determineSexX($threshold) { | |
// $x_snps = $this->getCount("X"); // Get the count of X chromosome SNPs | |
// if ($x_snps > 0) { | |
// if (count($this->heterozygous("X")) / $x_snps > $threshold) { | |
// return "Female"; // More heterozygous SNPs than the threshold, likely female | |
// } else { | |
// return "Male"; // Fewer heterozygous SNPs than the threshold, likely male | |
// } | |
// } else { | |
// return ""; // No X chromosome SNPs found | |
// } | |
// } | |
// /** | |
// * Determine the sex based on Y chromosome SNPs. | |
// * | |
// * @param float $threshold The threshold for determining sex. | |
// * @return string The determined sex ("Male", "Female") or an empty string if unknown. | |
// */ | |
// public function determineSexY($threshold) { | |
// $y_snps = $this->getCount("Y"); // Get the count of Y chromosome SNPs | |
// if ($y_snps > 0) { | |
// if (count($this->notnull("Y")) / $y_snps > $threshold) { | |
// return "Male"; // More non-null SNPs than the threshold, likely male | |
// } else { | |
// return "Female"; // Fewer non-null SNPs than the threshold, likely female | |
// } | |
// } else { | |
// return ""; // No Y chromosome SNPs found | |
// } | |
// } | |
// public function deduplicateRsids() | |
// { | |
// // Keep the first duplicate rsid | |
// $duplicateRsids = $this->_snps->duplicated("first"); | |
// // Save duplicate SNPs | |
// $this->_duplicate = array_merge($this->_duplicate, $this->_snps->where($duplicateRsids)); | |
// // Deduplicate | |
// $this->_snps = $this->_snps->where(!$duplicateRsids); | |
// } | |
// /** | |
// * Get the start and stop positions of the non-PAR region on a given chromosome. | |
// * | |
// * @param string $chrom The chromosome identifier. | |
// * @return array An array containing the start and stop positions of the non-PAR region. | |
// */ | |
// public function getNonParStartStop($chrom) { | |
// $pr = $this->getParRegions($this->build); // Get the PAR regions | |
// $np_start = $pr->filter(function ($item) use ($chrom) { | |
// return $item['chrom'] === $chrom && $item['region'] === "PAR1"; | |
// })->pluck('stop')->first(); // Get the stop position of PAR1 on the given chromosome | |
// $np_stop = $pr->filter(function ($item) use ($chrom) { | |
// return $item['chrom'] === $chrom && $item['region'] === "PAR2"; | |
// })->pluck('start')->first(); // Get the start position of PAR2 on the given chromosome | |
// return [$np_start, $np_stop]; // Return the start and stop positions of the non-PAR region | |
// } | |
// public function getNonParSnps($chrom, $heterozygous = true) | |
// { | |
// [$np_start, $np_stop] = $this->getNonParStartStop($chrom); // Get the start and stop positions of the non-PAR region | |
// $df = $this->filter($chrom); // Filter the data for the given chromosome | |
// if ($heterozygous) { | |
// // Get heterozygous SNPs in the non-PAR region (i.e., discrepant XY SNPs) | |
// return $df->filter(function ($row) use ($np_start, $np_stop) { | |
// return !is_null($row['genotype']) && | |
// strlen($row['genotype']) == 2 && | |
// $row['genotype'][0] != $row['genotype'][1] && | |
// $row['pos'] > $np_start && | |
// $row['pos'] < $np_stop; | |
// })->keys(); // Return the keys (indices) of the filtered SNPs | |
// } else { | |
// // Get homozygous SNPs in the non-PAR region | |
// return $df->filter(function ($row) use ($np_start, $np_stop) { | |
// return !is_null($row['genotype']) && | |
// strlen($row['genotype']) == 2 && | |
// $row['genotype'][0] == $row['genotype'][1] && | |
// $row['pos'] > $np_start && | |
// $row['pos'] < $np_stop; | |
// })->keys(); // Return the keys (indices) of the filtered SNPs | |
// } | |
// } | |
// public function deduplicateAlleles($rsids) | |
// { | |
// // Remove duplicate alleles | |
// $this->_snps = $this->_snps->map(function ($row) use ($rsids) { | |
// if (in_array($row["id"], $rsids)) { | |
// $row["genotype"] = $row["genotype"][0]; | |
// } | |
// return $row; | |
// }); | |
// } | |
// public function deduplicateSexChrom(string $chrom): void | |
// { | |
// // Deduplicate a chromosome in the non-PAR region. | |
// $discrepantXYSnps = $this->getNonParSnps($chrom); | |
// // Save discrepant XY SNPs | |
// $this->_discrepant_XY = array_merge( | |
// $this->_discrepant_XY, | |
// $this->_snps[$discrepantXYSnps] | |
// ); | |
// // Drop discrepant XY SNPs since it's ambiguous for which allele to deduplicate | |
// unset($this->_snps[$discrepantXYSnps]); | |
// // Get remaining non-PAR SNPs with two alleles | |
// $nonParSnps = $this->getNonParSnps($chrom, heterozygous: false); | |
// // Deduplicate the remaining non-PAR SNPs | |
// $this->deduplicateAlleles($nonParSnps); | |
// } | |
// public function deduplicateXYChrom(): void | |
// { | |
// // Fix chromosome issue where some data providers duplicate male X and Y chromosomes | |
// $this->deduplicateSexChrom("X"); | |
// $this->deduplicateSexChrom("Y"); | |
// } | |
// public function deduplicateMTChrom(): void | |
// { | |
// // Deduplicate MT chromosome. | |
// $heterozygousMTSnps = $this->_snps[$this->heterozygous("MT")->index] ?? []; | |
// // Save heterozygous MT SNPs | |
// $this->_heterozygous_MT = array_merge( | |
// $this->_heterozygous_MT, | |
// $this->_snps[$heterozygousMTSnps] | |
// ); | |
// // Drop heterozygous MT SNPs since it's ambiguous for which allele to deduplicate | |
// unset($this->_snps[$heterozygousMTSnps]); | |
// $this->deduplicateAlleles($this->homozygous("MT")->index); | |
// } | |
// function get_par_regions(int $build): array { | |
// $data = []; | |
// if ($build === 37) { | |
// $data = [ | |
// ["region" => "PAR1", "chrom" => "X", "start" => 60001, "stop" => 2699520], | |
// ["region" => "PAR2", "chrom" => "X", "start" => 154931044, "stop" => 155260560], | |
// ["region" => "PAR1", "chrom" => "Y", "start" => 10001, "stop" => 2649520], | |
// ["region" => "PAR2", "chrom" => "Y", "start" => 59034050, "stop" => 59363566], | |
// ]; | |
// } elseif ($build === 38) { | |
// $data = [ | |
// ["region" => "PAR1", "chrom" => "X", "start" => 10001, "stop" => 2781479], | |
// ["region" => "PAR2", "chrom" => "X", "start" => 155701383, "stop" => 156030895], | |
// ["region" => "PAR1", "chrom" => "Y", "start" => 10001, "stop" => 2781479], | |
// ["region" => "PAR2", "chrom" => "Y", "start" => 56887903, "stop" => 57217415], | |
// ]; | |
// } elseif ($build === 36) { | |
// $data = [ | |
// ["region" => "PAR1", "chrom" => "X", "start" => 1, "stop" => 2709520], | |
// ["region" => "PAR2", "chrom" => "X", "start" => 154584238, "stop" => 154913754], | |
// ["region" => "PAR1", "chrom" => "Y", "start" => 1, "stop" => 2709520], | |
// ["region" => "PAR2", "chrom" => "Y", "start" => 57443438, "stop" => 57772954], | |
// ]; | |
// } | |
// return $data; | |
// } | |
// function natural_sort_key(string $value): array { | |
// return preg_split("/(\D*\d+\D*)/", $value, 0, PREG_SPLIT_DELIM_CAPTURE); | |
// } | |
// function sort_snps(array &$snps): void { | |
// // Get unique chrom values | |
// $uniqueChromosomalValues = array_unique(array_column($snps, 'chrom')); | |
// // Sort uniqueChromosomalValues based on natural sorting | |
// usort($uniqueChromosomalValues, function ($a, $b) { | |
// return strnatcmp($a, $b); | |
// }); | |
// // Move PAR and MT to the end of sorted array | |
// if (($key = array_search("PAR", $uniqueChromosomalValues)) !== false) { | |
// unset($uniqueChromosomalValues[$key]); | |
// $uniqueChromosomalValues[] = "PAR"; | |
// } | |
// if (($key = array_search("MT", $uniqueChromosomalValues)) !== false) { | |
// unset($uniqueChromosomalValues[$key]); | |
// $uniqueChromosomalValues[] = "MT"; | |
// } | |
// // Sort snps based on uniqueChromosomalValues and pos | |
// usort($snps, function ($a, $b) use ($uniqueChromosomalValues) { | |
// $chromosomeA = array_search($a['chrom'], $uniqueChromosomalValues); | |
// $chromosomeB = array_search($b['chrom'], $uniqueChromosomalValues); | |
// if ($chromosomeA === $chromosomeB) { | |
// return $a['pos'] <=> $b['pos']; | |
// } | |
// return $chromosomeA <=> $chromosomeB; | |
// }); | |
// } | |
// public function remap(string $target_assembly, bool $complement_bases = true): array | |
// { | |
// $chromosomes_remapped = []; | |
// $chromosomes_not_remapped = []; | |
// $snps = $this->snps; | |
// // Check if there are SNPs to remap | |
// if ($snps->empty) { | |
// logger.warning("No SNPs to remap"); | |
// return [$chromosomes_remapped, $chromosomes_not_remapped]; | |
// } else { | |
// $chromosomes = $snps["chrom"]->unique(); | |
// $chromosomes_not_remapped = $chromosomes->values(); | |
// $valid_assemblies = ["NCBI36", "GRCh37", "GRCh38", 36, 37, 38]; | |
// // Validate target assembly | |
// if (!in_array($target_assembly, $valid_assemblies, true)) { | |
// logger.warning("Invalid target assembly"); | |
// return [$chromosomes_remapped, $chromosomes_not_remapped]; | |
// } | |
// // Convert target assembly to string format | |
// if (is_int($target_assembly)) { | |
// if ($target_assembly == 36) { | |
// $target_assembly = "NCBI36"; | |
// } else { | |
// $target_assembly = "GRCh" . strval($target_assembly); | |
// } | |
// } | |
// // Determine source assembly based on current build | |
// if ($this->build == 36) { | |
// $source_assembly = "NCBI36"; | |
// } else { | |
// $source_assembly = "GRCh" . strval($this->build); | |
// } | |
// // Check if source and target assemblies are the same | |
// if ($source_assembly === $target_assembly) { | |
// return [$chromosomes_remapped, $chromosomes_not_remapped]; | |
// } | |
// // Get assembly mapping data | |
// $assembly_mapping_data = $this->_resources->get_assembly_mapping_data($source_assembly, $target_assembly); | |
// if (!$assembly_mapping_data) { | |
// return [$chromosomes_remapped, $chromosomes_not_remapped]; | |
// } | |
// $tasks = []; | |
// foreach ($chromosomes as $chrom) { | |
// if (array_key_exists($chrom, $assembly_mapping_data)) { | |
// $chromosomes_remapped[] = $chrom; | |
// unset($chromosomes_not_remapped[array_search($chrom, $chromosomes_not_remapped)]); | |
// $mappings = $assembly_mapping_data[$chrom]; | |
// // Prepare remapping task | |
// $tasks[] = [ | |
// "snps" => $snps->loc[$snps["chrom"] === $chrom], | |
// "mappings" => $mappings, | |
// "complement_bases" => $complement_bases, | |
// ]; | |
// } else { | |
// // Chromosome not remapped, remove it from SNPs for consistency | |
// logger.warning("Chromosome {$chrom} not remapped; removing chromosome from SNPs for consistency"); | |
// $snps = $snps->drop($snps->loc[$snps["chrom"] === $chrom]->index); | |
// } | |
// } | |
// // Perform remapping in parallel | |
// $remapped_snps = $this->_parallelizer($this->_remapper, $tasks); | |
// $remapped_snps = pd.concat($remapped_snps); | |
// // Update remapped SNP positions and genotypes | |
// foreach ($remapped_snps['index'] as $index) { | |
// $snps['pos'][$index] = $remapped_snps['pos'][$index]; | |
// $snps['genotype'][$index] = $remapped_snps['genotype'][$index]; | |
// } | |
// foreach ($snps->pos as &$value) { | |
// $value = (int)$value; | |
// } | |
// // $snps->[$remapped_snps->index, "pos"] = $remapped_snps["pos"]; | |
// // $snps->loc[$remapped_snps->index, "genotype"] = $remapped_snps["genotype"]; | |
// // $snps->pos = $snps->pos->astype(np.uint32); | |
// // Update SNPs and rebuild index | |
// $this->_snps = $snps; | |
// $this->sort(); | |
// // Update the build with the target assembly | |
// $this->_build = intval(substr($target_assembly, -2)); | |
// return [$chromosomes_remapped, $chromosomes_not_remapped]; | |
// } | |
// } | |
// private function _remapper(array $task): DataFrame | |
// { | |
// $temp = $task["snps"]->copy(); | |
// $mappings = $task["mappings"]; | |
// $complement_bases = $task["complement_bases"]; | |
// $temp["remapped"] = false; | |
// $pos_start = intval($temp["pos"]->describe()["min"]); | |
// $pos_end = intval($temp["pos"]->describe()["max"]); | |
// foreach ($mappings["mappings"] as $mapping) { | |
// $orig_start = $mapping["original"]["start"]; | |
// $orig_end = $mapping["original"]["end"]; | |
// $mapped_start = $mapping["mapped"]["start"]; | |
// $mapped_end = $mapping["mapped"]["end"]; | |
// $orig_region = $mapping["original"]["seq_region_name"]; | |
// $mapped_region = $mapping["mapped"]["seq_region_name"]; | |
// // Skip if mapping is outside of range of SNP positions | |
// if ($orig_end < $pos_start || $orig_start > $pos_end) { | |
// continue; | |
// } | |
// // Find the SNPs that are being remapped for this mapping | |
// $snp_indices = $temp->loc[ | |
// !$temp["remapped"] | |
// & ($temp["pos"] >= $orig_start) | |
// & ($temp["pos"] <= $orig_end) | |
// ]->index; | |
// // If there are no SNPs here, skip | |
// if (count($snp_indices) === 0) { | |
// continue; | |
/** | |
* Matches SNPs with another SNPs object and returns a new SNPs object with the matching SNPs. | |
* | |
* @param SNPs $otherKit The other SNPs object to match with. | |
* @return SNPs A new SNPs object containing the matching SNPs. | |
*/ | |
public function matchWith(SNPs $otherKit): SNPs | |
{ | |
$matchedSnps = []; // Initialize an array to store the matched SNPs | |
// Iterate over the SNPs in the current object | |
foreach ($this->_snps as $snp) { | |
// Iterate over the SNPs in the other object | |
foreach ($otherKit->getSnps() as $otherSnp) { | |
// Check if the SNP positions and alleles match | |
if ($snp['pos'] == $otherSnp['pos'] && $snp['genotype'] == $otherSnp['genotype']) { | |
$matchedSnps[] = $snp; // Add the matching SNP to the array | |
} | |
} | |
} | |
// Create a new SNPs object with the matched SNPs | |
$matchedSnpsObject = new SNPs(); | |
$matchedSnpsObject->setSNPs($matchedSnps); | |
return $matchedSnpsObject; // Return the new SNPs object | |
} | |
// } | |
// $orig_range_len = $orig_end - $orig_start; | |
// $mapped_range_len = $mapped_end - $mapped_start; | |
// // If this would change chromosome, skip | |
// // TODO: Allow within normal chromosomes | |
// // TODO: Flatten patches | |
// if ($orig_region !== $mapped_region) { | |
// logger.warning( | |
// "discrepant chroms for " . count($snp_indices) . " SNPs from " . $orig_region . " to " . $mapped_region | |
// ); | |
// continue; | |
// } | |
// // If there is any stretching or squashing of the region | |
// // observed when mapping NCBI36 -> GRCh38 | |
// // TODO: Disallow skipping a version when remapping | |
// if ($orig_range_len !== $mapped_range_len) { | |
// logger.warning( | |
// "discrepant coords for " . count($snp_indices) . " SNPs from " . $orig_region . ":" . $orig_start . "-" . $orig_end . " to " . $mapped_region . ":" . $mapped_start . "-" . $mapped_end | |
// ); | |
// continue; | |
// } | |
// // Remap the SNPs | |
// if ($mapping['mapped']['strand'] === -1) { | |
// // Flip and (optionally) complement since we're mapping to the minus strand | |
// $diff_from_start = array_map(function($pos) use ($orig_start) { | |
// return $pos - $orig_start; | |
// }, $temp['pos'][$snp_indices]); | |
// $temp['pos'][$snp_indices] = array_map(function($diff) use ($mapped_end) { | |
// return $mapped_end - $diff; | |
// }, $diff_from_start); | |
// if ($complement_bases) { | |
// $temp['genotype'][$snp_indices] = array_map([$this, '_complement_bases'], $temp['genotype'][$snp_indices]); | |
// } | |
// } else { | |
// // Mapping is on the same (plus) strand, so just remap based on offset | |
// $offset = $mapped_start - $orig_start; | |
// $temp['pos'][$snp_indices] = array_map(function($pos) use ($offset) { | |
// return $pos + $offset; | |
// }, $temp['pos'][$snp_indices]); | |
// } | |
// // Mark these SNPs as remapped | |
// foreach ($snp_indices as $index) { | |
// $temp['remapped'][$index] = true; | |
// } | |
// } | |
// return $temp; | |
// } | |
// /** | |
// * Returns the complement of a given genotype string. | |
// * | |
// * @param string|null $genotype The genotype string to complement. | |
// * @return string|null The complement of the genotype string, or null if the input is null. | |
// */ | |
// function complement_bases(string|null $genotype): string|null | |
// { | |
// if (is_null($genotype)) { | |
// return null; | |
// } | |
// $complement = ""; // Variable to store the complement genotype string. | |
// // Iterate over each character in the genotype string. | |
// for ($i = 0; $i < strlen($genotype); $i++) { | |
// $base = $genotype[$i]; // Get the current base. | |
// // Determine the complement of the base and append it to the complement string. | |
// if ($base === "A") { | |
// $complement .= "T"; | |
// } elseif ($base === "G") { | |
// $complement .= "C"; | |
// } elseif ($base === "C") { | |
// $complement .= "G"; | |
// } elseif ($base === "T") { | |
// $complement .= "A"; | |
// } else { | |
// $complement .= $base; // If the base is not A, G, C, or T, keep it as is. | |
// } | |
// } | |
// return $complement; // Return the complement genotype string. | |
// } | |
// /** | |
// * Returns an array representing the natural sort order of a given string. | |
// * | |
// * @param string $s The string to generate the natural sort key for. | |
// * @return array An array representing the natural sort order of the string. | |
// */ | |
// function natural_sort_key(string $s): array | |
// { | |
// $natural_sort_re = '/([0-9]+)/'; // Regular expression pattern to match numbers in the string. | |
// // Split the string using the regular expression pattern and capture the delimiter. | |
// // Map each segment to its corresponding natural sort key value. | |
// return array_map( | |
// fn($text) => is_numeric($text) ? intval($text) : strtolower($text), | |
// preg_split($natural_sort_re, $s, -1, PREG_SPLIT_DELIM_CAPTURE) | |
// ); | |
// } | |
// /** | |
// * Merge SNP objects based on specified thresholds and options. | |
// * | |
// * @param array $snps_objects An array of SNP objects to merge. | |
// * @param int $discrepant_positions_threshold The threshold for the number of discrepant positions allowed. | |
// * @param int $discrepant_genotypes_threshold The threshold for the number of discrepant genotypes allowed. | |
// * @param bool $remap Whether to remap the merged SNP objects. | |
// * @param string $chrom The chromosome to merge SNP objects for. | |
// */ | |
// public function merge( | |
// array $snps_objects = [], | |
// int $discrepant_positions_threshold = 100, | |
// int $discrepant_genotypes_threshold = 500, | |
// bool $remap = true, | |
// string $chrom = "" | |
// ) {} | |
// // Your PHP code implementation here | |
// /** | |
// * Initializes the SNPs object with the properties of the SNPs object being merged. | |
// * | |
// * @param mixed $s The SNPs object being merged. | |
// */ | |
// public function init($s) | |
// { | |
// // Initialize properties of the SNPs object being merged | |
// $this->_snps = $s->snps; | |
// $this->_duplicate = $s->duplicate; | |
// $this->_discrepant_XY = $s->discrepant_XY; | |
// $this->_heterozygous_MT = $s->heterozygous_MT; | |
// $this->_discrepant_vcf_position = $s->discrepant_vcf_position; | |
// $this->_discrepant_merge_positions = $s->discrepant_merge_positions; | |
// $this->_discrepant_merge_genotypes = $s->discrepant_merge_genotypes; | |
// $this->_source = $s->_source; | |
// $this->_phased = $s->phased; | |
// $this->_build = $s->build; | |
// $this->_build_detected = $s->build_detected; | |
// } | |
// /** | |
// * Ensures that the builds match when merging SNPs objects. | |
// * | |
// * @param mixed $s The SNPs object being merged. | |
// */ | |
// public function ensure_same_build($s) | |
// { | |
// // Ensure builds match when merging | |
// if (!$s->build_detected) { | |
// $this->logger->warning(sprintf("Build not detected for %s, assuming Build %s", | |
// $s->__toString(), | |
// $s->build | |
// )); | |
// } | |
// if ($this->build != $s->build) { | |
// $this->logger->info(sprintf("%s has Build %s; remapping to Build %s", | |
// $s->__toString(), | |
// $s->build, | |
// $this->build | |
// )); | |
// $s->remap($this->build); | |
// } | |
// } | |
// /** | |
// * Merges the properties of the SNPs object being merged. | |
// * | |
// * @param mixed $s The SNPs object being merged. | |
// */ | |
// public function merge_properties($s) | |
// { | |
// if (!$s->build_detected) { | |
// // Can no longer assume build has been detected for all SNPs after merge | |
// $this->_build_detected = false; | |
// } | |
// if (!$s->phased) { | |
// // Can no longer assume all SNPs are phased after merge | |
// $this->_phased = false; | |
// } | |
// $this->_source = array_merge($this->_source, $s->_source); | |
// } | |
// /** | |
// * Merges the dataframes of the SNPs object being merged. | |
// * | |
// * @param SNPs $s The SNPs object being merged. | |
// */ | |
// public function merge_dfs(SNPs $s): void | |
// { | |
// // Append dataframes created when a "SNPs" object is instantiated | |
// $this->_duplicate = array_merge($this->_duplicate, $s->_duplicate); | |
// $this->_discrepant_XY = array_merge($this->_discrepant_XY, $s->_discrepant_XY); | |
// $this->_heterozygous_MT = array_merge($this->_heterozygous_MT, $s->_heterozygous_MT); | |
// $this->_discrepant_vcf_position = array_merge($this->_discrepant_vcf_position, $s->_discrepant_vcf_position); | |
// } | |
// public function merge_snps(SNPs $s, int $positions_threshold, int $genotypes_threshold, string $merge_chrom): array | |
// { | |
// // Merge SNPs, identifying those with discrepant positions and genotypes; update NAs | |
// // Identify common SNPs (i.e., any rsids being added that already exist in self.snps) | |
// $df = (!$merge_chrom) ? | |
// $this->snps->join($s->snps, "inner", null, "_added") : | |
// $this->snps->where("chrom", "=", $merge_chrom) | |
// ->join($s->snps->where("chrom", "=", $merge_chrom), "inner", null, "_added"); | |
// $common_rsids = $df->getIndex(); | |
// $discrepant_positions = $df->where( | |
// fn($row) => $row["chrom"] != $row["chrom_added"] || $row["pos"] != $row["pos_added"] | |
// ); | |
// if (count($discrepant_positions) >= $positions_threshold) { | |
// $this->logger->warning("Too many SNPs differ in position; ensure SNPs have the same build"); | |
// return [false]; | |
// } | |
// // Remove null genotypes | |
// $df = $df->where(fn($row) => !is_null($row["genotype"]) && !is_null($row["genotype_added"])); | |
// // Discrepant genotypes are where alleles are not equivalent (i.e., alleles are not the same and not swapped) | |
// $discrepant_genotypes = $df->where(fn($row) => | |
// strlen($row["genotype"]) != strlen($row["genotype_added"]) || | |
// ( | |
// strlen($row["genotype"]) == 1 && | |
// strlen($row["genotype_added"]) == 1 && | |
// $row["genotype"] != $row["genotype_added"] | |
// ) || | |
// ( | |
// strlen($row["genotype"]) == 2 && | |
// strlen($row["genotype_added"]) == 2 && | |
// !( | |
// $row["genotype"][0] == $row["genotype_added"][0] && | |
// $row["genotype"][1] == $row["genotype_added"][1] | |
// ) && | |
// !( | |
// $row["genotype"][0] == $row["genotype_added"][1] && | |
// $row["genotype"][1] == $row["genotype_added"][0] | |
// ) | |
// ) | |
// ); | |
// if (count($discrepant_genotypes) >= $genotypes_threshold) { | |
// $this->logger->warning("Too many SNPs differ in their genotype; ensure the file is for the same individual"); | |
// return [false]; | |
// } | |
// // Add new SNPs | |
// $this->_snps = (!$merge_chrom) ? | |
// $this->snps->combineFirst($s->snps) : | |
// $this->snps->combineFirst($s->snps->where("chrom", "=", $merge_chrom)); | |
// // Convert position back to uint32 after combineFirst | |
// $this->_snps["pos"] = $this->snps["pos"]->cast(UInt32::class); | |
// if (0 < count($discrepant_positions) && count($discrepant_positions) < $positions_threshold) { | |
// $this->logger->warning(count($discrepant_positions)." SNP positions were discrepant; keeping original positions"); | |
// } | |
// if (0 < count($discrepant_genotypes) && count($discrepant_genotypes) < $genotypes_threshold) { | |
// $this->logger->warning(count($discrepant_genotypes)." SNP genotypes were discrepant; marking those as null"); | |
// } | |
// // Set discrepant genotypes to null | |
// $this->_snps->whereIndexIn($discrepant_genotypes->getIndex())->set("genotype", null); | |
// // Append discrepant positions dataframe | |
// $this->_discrepant_merge_positions = $this->_discrepant_merge_positions | |
// ->concat($discrepant_positions, true); | |
// // Append discrepant genotypes dataframe | |
// $this->_discrepant_merge_genotypes = $this->_discrepant_merge_genotypes | |
// ->concat($discrepant_genotypes, true); | |
// return [ | |
// true, | |
// [ | |
// "common_rsids" => $common_rsids, | |
// "discrepant_position_rsids" => $discrepant_positions->getIndex(), | |
// "discrepant_genotype_rsids" => $discrepant_genotypes->getIndex(), | |
// ], | |
// ]; | |
// $results = []; | |
// foreach ($snps_objects as $snps_object) { | |
// $d = [ | |
// "merged" => false, | |
// "common_rsids" => [], | |
// "discrepant_position_rsids" => [], | |
// "discrepant_genotype_rsids" => [], | |
// ]; | |
// if (!$snps_object->valid) { | |
// $this->logger->warning("No SNPs to merge..."); | |
// $results[] = $d; | |
// continue; | |
// } | |
// if (!$this->valid) { | |
// $this->logger->info("Loading ".$snps_object->__toString()); | |
// $this->init($snps_object); | |
// $d["merged"] = true; | |
// } else { | |
// $this->logger->info("Merging ".$snps_object->__toString()); | |
// if ($remap) { | |
// $this->ensure_same_build($snps_object); | |
// } | |
// if ($this->build != $snps_object->build) { | |
// $this->logger->warning( | |
// $snps_object->__toString()." has Build ".$snps_object->build."; this SNPs object has Build ".$this->build | |
// ); | |
// } | |
// [$merged, $extra] = $this->merge_snps( | |
// $snps_object, | |
// $discrepant_positions_threshold, | |
// $discrepant_genotypes_threshold, | |
// $chrom | |
// ); | |
// if ($merged) { | |
// $this->merge_properties($snps_object); | |
// $this->merge_dfs($snps_object); | |
// $this->sort(); | |
// $d["merged"] = true; | |
// $d = array_merge($d, $extra); | |
// } | |
// } | |
// $results[] = $d; | |
// } | |
// return $results; | |
// } | |
// public function sortSnps() | |
// { | |
// // Deprecated method. Display a deprecation error. | |
// trigger_error("This method has been renamed to `sort`.", E_USER_DEPRECATED); | |
// // Call the new method `sort`. | |
// $this->sort(); | |
// } | |
// public function remapSnps($target_assembly, $complement_bases = true) | |
// { | |
// // Deprecated method. Display a deprecation error. | |
// trigger_error("This method has been renamed to `remap`.", E_USER_DEPRECATED); | |
// // Call the new method `remap` and return the result. | |
// return $this->remap($target_assembly, $complement_bases); | |
// } | |
// public function saveSnps($filename = "", $vcf = false, $atomic = true, ...$kwargs) | |
// { | |
// // Deprecated method. Display a deprecation error. | |
// trigger_error( | |
// "Method `save_snps` has been replaced by `to_csv`, `to_tsv`, and `to_vcf`.", | |
// E_USER_DEPRECATED | |
// ); | |
// // Call the private method `_save` with the provided arguments and return the result. | |
// return $this->_save($filename, $vcf, $atomic, ...$kwargs); | |
// } | |
// public function getSnpCount($chrom = "") | |
// { | |
// // Deprecated method. Display a deprecation error. | |
// trigger_error( | |
// "This method has been renamed to `get_count`.", | |
// E_USER_DEPRECATED | |
// ); | |
// // Call the new method `getCount` with the provided argument and return the result. | |
// return $this->getCount($chrom); | |
// } | |
// public function notNullSnps($chrom = "") | |
// { | |
// // Deprecated method. Display a deprecation error. | |
// trigger_error("This method has been renamed to `notnull`.", E_USER_DEPRECATED); | |
// // Call the new method `notnull` with the provided argument and return the result. | |
// return $this->notnull($chrom); | |
// } | |
// public function getSummary() // Also rename this method to match the property | |
// { | |
// // Deprecated method. Display a deprecation error. | |
// trigger_error("This method has been renamed to `summary` and is now a property.", E_USER_DEPRECATED); | |
// // Return the value of the `summary` property. | |
// return $this->summary; | |
// } | |
// public function getAssembly() | |
// { | |
// // Deprecated method. Display a deprecation error. | |
// trigger_error("See the `assembly` property.", E_USER_DEPRECATED); | |
// // Return the value of the `assembly` property. | |
// return $this->assembly; | |
// } | |
// public function getChromosomes() | |
// { | |
// // Deprecated method. Display a deprecation error. | |
// trigger_error("See the `chromosomes` property.", E_USER_DEPRECATED); | |
// // Return the value of the `chromosomes` property. | |
// return $this->chromosomes; | |
// } | |
// public function getChromosomesSummary() | |
// { | |
// // Deprecated method. Display a deprecation error. | |
// trigger_error("See the `chromosomes_summary` property.", E_USER_DEPRECATED); | |
// // Return the value of the `chromosomes_summary` property. | |
// return $this->chromosomes_summary; | |
// } | |
// /** | |
// * Computes cluster overlap based on given threshold. | |
// * | |
// * @param float $cluster_overlap_threshold The threshold for cluster overlap. | |
// * @return DataFrame The computed cluster overlap DataFrame. | |
// */ | |
// public function compute_cluster_overlap($cluster_overlap_threshold = 0.95) { | |
// // Sample data for cluster overlap computation | |
// $data = [ | |
// "cluster_id" => ["c1", "c3", "c4", "c5", "v5"], | |
// "company_composition" => [ | |
// "23andMe-v4", | |
// "AncestryDNA-v1, FTDNA, MyHeritage", | |
// "23andMe-v3", | |
// "AncestryDNA-v2", | |
// "23andMe-v5, LivingDNA", | |
// ], | |
// "chip_base_deduced" => [ | |
// "HTS iSelect HD", | |
// "OmniExpress", | |
// "OmniExpress plus", | |
// "OmniExpress plus", | |
// "Illumina GSAs", | |
// ], | |
// "snps_in_cluster" => array_fill(0, 5, 0), | |
// "snps_in_common" => array_fill(0, 5, 0), | |
// ]; | |
// // Create a DataFrame from the data and set "cluster_id" as the index | |
// $df = new DataFrame($data); | |
// $df->setIndex("cluster_id"); | |
// $to_remap = null; | |
// if ($this->build != 37) { | |
// // Create a clone of the current object for remapping | |
// $to_remap = clone $this; | |
// $to_remap->remap(37); // clusters are relative to Build 37 | |
// $self_snps = $to_remap->snps()->select(["chrom", "pos"])->dropDuplicates(); | |
// } else { | |
// $self_snps = $this->snps()->select(["chrom", "pos"])->dropDuplicates(); | |
// } | |
// // Retrieve chip clusters from resources | |
// $chip_clusters = $this->resources->get_chip_clusters(); | |
// // Iterate over each cluster in the DataFrame | |
// foreach ($df->indexValues() as $cluster) { | |
// // Filter chip clusters based on the current cluster | |
// $cluster_snps = $chip_clusters->filter(function ($row) use ($cluster) { | |
// return strpos($row["clusters"], $cluster) !== false; | |
// })->select(["chrom", "pos"]); | |
// // Update the DataFrame with the number of SNPs in the cluster and in common with the current object | |
// $df->loc[$cluster]["snps_in_cluster"] = count($cluster_snps); | |
// $df->loc[$cluster]["snps_in_common"] = count($self_snps->merge($cluster_snps, "inner")); | |
// // Calculate overlap ratios for cluster and self | |
// $df["overlap_with_cluster"] = $df["snps_in_common"] / $df["snps_in_cluster"]; | |
// $df["overlap_with_self"] = $df["snps_in_common"] / count($self_snps); | |
// // Find the cluster with the maximum overlap | |
// $max_overlap = array_keys($df["overlap_with_cluster"], max($df["overlap_with_cluster"]))[0]; | |
// // Check if the maximum overlap exceeds the threshold for both cluster and self | |
// if ( | |
// $df["overlap_with_cluster"][$max_overlap] > $cluster_overlap_threshold && | |
// $df["overlap_with_self"][$max_overlap] > $cluster_overlap_threshold | |
// ) { | |
// // Update the current object's cluster and chip based on the maximum overlap | |
// $this->cluster = $max_overlap; | |
// $this->chip = $df["chip_base_deduced"][$max_overlap]; | |
// $company_composition = $df["company_composition"][$max_overlap]; | |
// // Check if the current object's source is present in the company composition | |
// if (strpos($company_composition, $this->source) !== false) { | |
// if ($this->source === "23andMe" || $this->source === "AncestryDNA") { | |
// // Extract the chip version from the company composition | |
// $i = strpos($company_composition, "v"); | |
// $this->chip_version = substr($company_composition, $i, $i + 2); | |
// } | |
// } else { | |
// // Log a warning about the SNPs data source not found | |
// } | |
// } | |
// } | |
// // Return the computed cluster overlap DataFrame | |
// return $df; | |
// } | |
// } | |
/** | |
* Computes cluster overlap based on given threshold. | |
* | |
* @param float $cluster_overlap_threshold The threshold for cluster overlap. | |
* @return array The computed cluster overlap DataFrame. | |
*/ | |
public function computeClusterOverlap($cluster_overlap_threshold = 0.95): array { | |
// Sample data for cluster overlap computation | |
$data = [ | |
"cluster_id" => ["c1", "c3", "c4", "c5", "v5"], | |
"company_composition" => [ | |
"23andMe-v4", | |
"AncestryDNA-v1, FTDNA, MyHeritage", | |
"23andMe-v3", | |
"AncestryDNA-v2", | |
"23andMe-v5, LivingDNA", | |
], | |
"chip_base_deduced" => [ | |
"HTS iSelect HD", | |
"OmniExpress", | |
"OmniExpress plus", | |
"OmniExpress plus", | |
"Illumina GSAs", | |
], | |
"snps_in_cluster" => array_fill(0, 5, 0), | |
"snps_in_common" => array_fill(0, 5, 0), | |
]; | |
// Create a DataFrame from the data and set "cluster_id" as the index | |
$df = new DataFrame($data); | |
$df->setIndex("cluster_id"); | |
$to_remap = null; | |
if ($this->build != 37) { | |
// Create a clone of the current object for remapping | |
$to_remap = clone $this; | |
$to_remap->remap(37); // clusters are relative to Build 37 | |
$self_snps = $to_remap->snps()->select(["chrom", "pos"])->dropDuplicates(); | |
} else { | |
$self_snps = $this->snps()->select(["chrom", "pos"])->dropDuplicates(); | |
} | |
// Retrieve chip clusters from resources | |
$chip_clusters = $this->resources->get_chip_clusters(); | |
// Iterate over each cluster in the DataFrame | |
foreach ($df->indexValues() as $cluster) { | |
// Filter chip clusters based on the current cluster | |
$cluster_snps = $chip_clusters->filter(function ($row) use ($cluster) { | |
return strpos($row["clusters"], $cluster) !== false; | |
})->select(["chrom", "pos"]); | |
// Update the DataFrame with the number of SNPs in the cluster and in common with the current object | |
$df->loc[$cluster]["snps_in_cluster"] = count($cluster_snps); | |
$df->loc[$cluster]["snps_in_common"] = count($self_snps->merge($cluster_snps, "inner")); | |
// Calculate overlap ratios for cluster and self | |
$df["overlap_with_cluster"] = $df["snps_in_common"] / $df["snps_in_cluster"]; | |
$df["overlap_with_self"] = $df["snps_in_common"] / count($self_snps); | |
// Find the cluster with the maximum overlap | |
$max_overlap = array_keys($df["overlap_with_cluster"], max($df["overlap_with_cluster"]))[0]; | |
// Check if the maximum overlap exceeds the threshold for both cluster and self | |
if ( | |
$df["overlap_with_cluster"][$max_overlap] > $cluster_overlap_threshold && | |
$df["overlap_with_self"][$max_overlap] > $cluster_overlap_threshold | |
) { | |
// Update the current object's cluster and chip based on the maximum overlap | |
$this->cluster = $max_overlap; | |
$this->chip = $df["chip_base_deduced"][$max_overlap]; | |
$company_composition = $df["company_composition"][$max_overlap]; | |
// Check if the current object's source is present in the company composition | |
if (strpos($company_composition, $this->source) !== false) { | |
if ($this->source === "23andMe" || $this->source === "AncestryDNA") { | |
// Extract the chip version from the company composition | |
$i = strpos($company_composition, "v"); | |
$this->chip_version = substr($company_composition, $i, $i + 2); | |
} | |
} else { | |
// Log a warning about the SNPs data source not found | |
} | |
} | |
} | |
// Return the computed cluster overlap DataFrame | |
return $df; |
Lines 1 to 84 in 3d5b908
<?php | |
require_once 'snps.php'; | |
require_once 'snps/utils.php'; | |
class Individual extends SNPs | |
{ | |
/** | |
* Object used to represent and interact with an individual. | |
* | |
* The ``Individual`` object maintains information about an individual. The object provides | |
* methods for loading an individual's genetic data (SNPs) and normalizing it for use with the | |
* `lineage` framework. | |
* | |
* ``Individual`` inherits from ``snps.SNPs``. | |
*/ | |
private string $_name; | |
public function __construct(string $name, mixed $raw_data = [], array $kwargs = []) | |
{ | |
/** | |
* Initialize an ``Individual`` object. | |
* | |
* Parameters | |
* ---------- | |
* name : str | |
* name of the individual | |
* raw_data : str, bytes, ``SNPs`` (or list or tuple thereof) | |
* path(s) to file(s), bytes, or ``SNPs`` object(s) with raw genotype data | |
* kwargs : array | |
* parameters to ``snps.SNPs`` and/or ``snps.SNPs.merge`` | |
*/ | |
$this->_name = $name; | |
$init_args = $this->_get_defined_kwargs(new ReflectionMethod(SNPs::class, '__construct'), $kwargs); | |
$merge_args = $this->_get_defined_kwargs(new ReflectionMethod(SNPs::class, ''), $kwargs); | |
parent::__construct(...array_values($init_args)); | |
if (!is_array($raw_data)) { | |
$raw_data = [$raw_data]; | |
} | |
foreach ($raw_data as $file) { | |
$s = $file instanceof SNPs ? $file : new SNPs($file, ...array_values($init_args)); | |
$this->merge([$s], ...array_values($merge_args)); | |
} | |
} | |
private function _get_defined_kwargs(ReflectionMethod $callable, array $kwargs): array | |
{ | |
$parameters = $callable->getParameters(); | |
$defined_kwargs = []; | |
foreach ($parameters as $parameter) { | |
$name = $parameter->getName(); | |
if (array_key_exists($name, $kwargs)) { | |
$defined_kwargs[$name] = $kwargs[$name]; | |
} | |
} | |
return $defined_kwargs; | |
} | |
public function __toString(): string | |
{ | |
return sprintf("Individual('%s')", $this->_name); | |
} | |
public function getName(): string | |
{ | |
/** | |
* Get this ``Individual``'s name. | |
* | |
* Returns | |
* ------- | |
* str | |
*/ | |
return $this->_name; | |
} | |
public function getVarName(): string | |
{ | |
return clean_str($this->_name); |
Lines 1 to 33 in 3d5b908
<?php | |
require_once 'SNPs.php'; | |
class Triangulation { | |
public static function compareMultipleKits(array $kitsData): array { | |
$snpsLists = self::extractSnpLists($kitsData); | |
$commonSnps = self::findCommonSnps($snpsLists); | |
return self::filterNonCommonSnps($commonSnps, $kitsData); | |
} | |
private static function extractSnpLists(array $kitsData): array { | |
return array_map(function($kit) { return $kit->getSnps(); }, $kitsData); | |
} | |
private static function findCommonSnps(array $snpsLists): array { | |
return call_user_func_array('array_intersect_key', $snpsLists); | |
} | |
private static function filterNonCommonSnps(array $commonSnps, array $kitsData): array { | |
return array_filter($commonSnps, function($snp) use ($kitsData) { | |
return self::isSnpCommonAcrossAllKits($snp, $kitsData); | |
}); | |
} | |
private static function isSnpCommonAcrossAllKits(array $snp, array $kitsData): bool { | |
return count(array_filter($kitsData, function($kit) use ($snp) { | |
$snps = $kit->getSnps(); | |
return isset($snps[$snp['pos']]) && $snps[$snp['pos']]['genotype'] === $snp['genotype']; | |
})) === count($kitsData); | |
} | |
} |
Lines 1 to 28 in 3d5b908
{ | |
"name": "liberu-genealogy/php-dna", | |
"description": "lineage for PHP 8.3+", | |
"type": "library", | |
"keywords": ["dna","genotype"], | |
"homepage": "http://github.com/familytree365/php-dna", | |
"license": "MIT", | |
"require": { | |
"php": ">=8.3", | |
"league/csv": "^9.0", | |
"guzzlehttp/guzzle": "^7.2", | |
"symfony/http-client": "^7.0" | |
}, | |
"require-dev": { | |
"phpunit/phpunit": "^10.0", | |
"squizlabs/php_codesniffer": "3.6.*", | |
"rector/rector": "^0.11.49" | |
}, | |
"autoload": { | |
"psr-4": { | |
"Dna\\": "src/" | |
} | |
}, | |
"autoload-dev": { | |
"psr-4": { | |
"DnaTest\\": "tests/" | |
} | |
} |
Lines 1 to 2493 in 3d5b908
{ | |
"_readme": [ | |
"This file locks the dependencies of your project to a known state", | |
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", | |
"This file is @generated automatically" | |
], | |
"content-hash": "fca285b302d24e99deb5fd0903121911", | |
"packages": [ | |
{ | |
"name": "guzzlehttp/guzzle", | |
"version": "7.7.0", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/guzzle/guzzle.git", | |
"reference": "fb7566caccf22d74d1ab270de3551f72a58399f5" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/fb7566caccf22d74d1ab270de3551f72a58399f5", | |
"reference": "fb7566caccf22d74d1ab270de3551f72a58399f5", | |
"shasum": "" | |
}, | |
"require": { | |
"ext-json": "*", | |
"guzzlehttp/promises": "^1.5.3 || ^2.0", | |
"guzzlehttp/psr7": "^1.9.1 || ^2.4.5", | |
"php": "^7.2.5 || ^8.0", | |
"psr/http-client": "^1.0", | |
"symfony/deprecation-contracts": "^2.2 || ^3.0" | |
}, | |
"provide": { | |
"psr/http-client-implementation": "1.0" | |
}, | |
"require-dev": { | |
"bamarni/composer-bin-plugin": "^1.8.1", | |
"ext-curl": "*", | |
"php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", | |
"php-http/message-factory": "^1.1", | |
"phpunit/phpunit": "^8.5.29 || ^9.5.23", | |
"psr/log": "^1.1 || ^2.0 || ^3.0" | |
}, | |
"suggest": { | |
"ext-curl": "Required for CURL handler support", | |
"ext-intl": "Required for Internationalized Domain Name (IDN) support", | |
"psr/log": "Required for using the Log middleware" | |
}, | |
"type": "library", | |
"extra": { | |
"bamarni-bin": { | |
"bin-links": true, | |
"forward-command": false | |
} | |
}, | |
"autoload": { | |
"files": [ | |
"src/functions_include.php" | |
], | |
"psr-4": { | |
"GuzzleHttp\\": "src/" | |
} | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"MIT" | |
], | |
"authors": [ | |
{ | |
"name": "Graham Campbell", | |
"email": "[email protected]", | |
"homepage": "https://github.com/GrahamCampbell" | |
}, | |
{ | |
"name": "Michael Dowling", | |
"email": "[email protected]", | |
"homepage": "https://github.com/mtdowling" | |
}, | |
{ | |
"name": "Jeremy Lindblom", | |
"email": "[email protected]", | |
"homepage": "https://github.com/jeremeamia" | |
}, | |
{ | |
"name": "George Mponos", | |
"email": "[email protected]", | |
"homepage": "https://github.com/gmponos" | |
}, | |
{ | |
"name": "Tobias Nyholm", | |
"email": "[email protected]", | |
"homepage": "https://github.com/Nyholm" | |
}, | |
{ | |
"name": "Márk Sági-Kazár", | |
"email": "[email protected]", | |
"homepage": "https://github.com/sagikazarmark" | |
}, | |
{ | |
"name": "Tobias Schultze", | |
"email": "[email protected]", | |
"homepage": "https://github.com/Tobion" | |
} | |
], | |
"description": "Guzzle is a PHP HTTP client library", | |
"keywords": [ | |
"client", | |
"curl", | |
"framework", | |
"http", | |
"http client", | |
"psr-18", | |
"psr-7", | |
"rest", | |
"web service" | |
], | |
"support": { | |
"issues": "https://github.com/guzzle/guzzle/issues", | |
"source": "https://github.com/guzzle/guzzle/tree/7.7.0" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/GrahamCampbell", | |
"type": "github" | |
}, | |
{ | |
"url": "https://github.com/Nyholm", | |
"type": "github" | |
}, | |
{ | |
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", | |
"type": "tidelift" | |
} | |
], | |
"time": "2023-05-21T14:04:53+00:00" | |
}, | |
{ | |
"name": "guzzlehttp/promises", | |
"version": "2.0.0", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/guzzle/promises.git", | |
"reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/guzzle/promises/zipball/3a494dc7dc1d7d12e511890177ae2d0e6c107da6", | |
"reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6", | |
"shasum": "" | |
}, | |
"require": { | |
"php": "^7.2.5 || ^8.0" | |
}, | |
"require-dev": { | |
"bamarni/composer-bin-plugin": "^1.8.1", | |
"phpunit/phpunit": "^8.5.29 || ^9.5.23" | |
}, | |
"type": "library", | |
"extra": { | |
"bamarni-bin": { | |
"bin-links": true, | |
"forward-command": false | |
} | |
}, | |
"autoload": { | |
"psr-4": { | |
"GuzzleHttp\\Promise\\": "src/" | |
} | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"MIT" | |
], | |
"authors": [ | |
{ | |
"name": "Graham Campbell", | |
"email": "[email protected]", | |
"homepage": "https://github.com/GrahamCampbell" | |
}, | |
{ | |
"name": "Michael Dowling", | |
"email": "[email protected]", | |
"homepage": "https://github.com/mtdowling" | |
}, | |
{ | |
"name": "Tobias Nyholm", | |
"email": "[email protected]", | |
"homepage": "https://github.com/Nyholm" | |
}, | |
{ | |
"name": "Tobias Schultze", | |
"email": "[email protected]", | |
"homepage": "https://github.com/Tobion" | |
} | |
], | |
"description": "Guzzle promises library", | |
"keywords": [ | |
"promise" | |
], | |
"support": { | |
"issues": "https://github.com/guzzle/promises/issues", | |
"source": "https://github.com/guzzle/promises/tree/2.0.0" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/GrahamCampbell", | |
"type": "github" | |
}, | |
{ | |
"url": "https://github.com/Nyholm", | |
"type": "github" | |
}, | |
{ | |
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", | |
"type": "tidelift" | |
} | |
], | |
"time": "2023-05-21T13:50:22+00:00" | |
}, | |
{ | |
"name": "guzzlehttp/psr7", | |
"version": "2.5.0", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/guzzle/psr7.git", | |
"reference": "b635f279edd83fc275f822a1188157ffea568ff6" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/guzzle/psr7/zipball/b635f279edd83fc275f822a1188157ffea568ff6", | |
"reference": "b635f279edd83fc275f822a1188157ffea568ff6", | |
"shasum": "" | |
}, | |
"require": { | |
"php": "^7.2.5 || ^8.0", | |
"psr/http-factory": "^1.0", | |
"psr/http-message": "^1.1 || ^2.0", | |
"ralouphie/getallheaders": "^3.0" | |
}, | |
"provide": { | |
"psr/http-factory-implementation": "1.0", | |
"psr/http-message-implementation": "1.0" | |
}, | |
"require-dev": { | |
"bamarni/composer-bin-plugin": "^1.8.1", | |
"http-interop/http-factory-tests": "^0.9", | |
"phpunit/phpunit": "^8.5.29 || ^9.5.23" | |
}, | |
"suggest": { | |
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" | |
}, | |
"type": "library", | |
"extra": { | |
"bamarni-bin": { | |
"bin-links": true, | |
"forward-command": false | |
} | |
}, | |
"autoload": { | |
"psr-4": { | |
"GuzzleHttp\\Psr7\\": "src/" | |
} | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"MIT" | |
], | |
"authors": [ | |
{ | |
"name": "Graham Campbell", | |
"email": "[email protected]", | |
"homepage": "https://github.com/GrahamCampbell" | |
}, | |
{ | |
"name": "Michael Dowling", | |
"email": "[email protected]", | |
"homepage": "https://github.com/mtdowling" | |
}, | |
{ | |
"name": "George Mponos", | |
"email": "[email protected]", | |
"homepage": "https://github.com/gmponos" | |
}, | |
{ | |
"name": "Tobias Nyholm", | |
"email": "[email protected]", | |
"homepage": "https://github.com/Nyholm" | |
}, | |
{ | |
"name": "Márk Sági-Kazár", | |
"email": "[email protected]", | |
"homepage": "https://github.com/sagikazarmark" | |
}, | |
{ | |
"name": "Tobias Schultze", | |
"email": "[email protected]", | |
"homepage": "https://github.com/Tobion" | |
}, | |
{ | |
"name": "Márk Sági-Kazár", | |
"email": "[email protected]", | |
"homepage": "https://sagikazarmark.hu" | |
} | |
], | |
"description": "PSR-7 message implementation that also provides common utility methods", | |
"keywords": [ | |
"http", | |
"message", | |
"psr-7", | |
"request", | |
"response", | |
"stream", | |
"uri", | |
"url" | |
], | |
"support": { | |
"issues": "https://github.com/guzzle/psr7/issues", | |
"source": "https://github.com/guzzle/psr7/tree/2.5.0" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/GrahamCampbell", | |
"type": "github" | |
}, | |
{ | |
"url": "https://github.com/Nyholm", | |
"type": "github" | |
}, | |
{ | |
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", | |
"type": "tidelift" | |
} | |
], | |
"time": "2023-04-17T16:11:26+00:00" | |
}, | |
{ | |
"name": "league/csv", | |
"version": "9.9.0", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/thephpleague/csv.git", | |
"reference": "b4418ede47fbd88facc34e40a16c8ce9153b961b" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/thephpleague/csv/zipball/b4418ede47fbd88facc34e40a16c8ce9153b961b", | |
"reference": "b4418ede47fbd88facc34e40a16c8ce9153b961b", | |
"shasum": "" | |
}, | |
"require": { | |
"ext-json": "*", | |
"ext-mbstring": "*", | |
"php": "^8.1.2" | |
}, | |
"require-dev": { | |
"doctrine/collections": "^2.1.2", | |
"ext-dom": "*", | |
"ext-xdebug": "*", | |
"friendsofphp/php-cs-fixer": "^v3.14.3", | |
"phpbench/phpbench": "^1.2.8", | |
"phpstan/phpstan": "^1.10.4", | |
"phpstan/phpstan-deprecation-rules": "^1.1.2", | |
"phpstan/phpstan-phpunit": "^1.3.10", | |
"phpstan/phpstan-strict-rules": "^1.5.0", | |
"phpunit/phpunit": "^10.0.14" | |
}, | |
"suggest": { | |
"ext-dom": "Required to use the XMLConverter and the HTMLConverter classes", | |
"ext-iconv": "Needed to ease transcoding CSV using iconv stream filters" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-master": "9.x-dev" | |
} | |
}, | |
"autoload": { | |
"files": [ | |
"src/functions_include.php" | |
], | |
"psr-4": { | |
"League\\Csv\\": "src" | |
} | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"MIT" | |
], | |
"authors": [ | |
{ | |
"name": "Ignace Nyamagana Butera", | |
"email": "[email protected]", | |
"homepage": "https://github.com/nyamsprod/", | |
"role": "Developer" | |
} | |
], | |
"description": "CSV data manipulation made easy in PHP", | |
"homepage": "https://csv.thephpleague.com", | |
"keywords": [ | |
"convert", | |
"csv", | |
"export", | |
"filter", | |
"import", | |
"read", | |
"transform", | |
"write" | |
], | |
"support": { | |
"docs": "https://csv.thephpleague.com", | |
"issues": "https://github.com/thephpleague/csv/issues", | |
"rss": "https://github.com/thephpleague/csv/releases.atom", | |
"source": "https://github.com/thephpleague/csv" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/sponsors/nyamsprod", | |
"type": "github" | |
} | |
], | |
"time": "2023-03-11T15:57:12+00:00" | |
}, | |
{ | |
"name": "psr/http-client", | |
"version": "1.0.2", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/php-fig/http-client.git", | |
"reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/php-fig/http-client/zipball/0955afe48220520692d2d09f7ab7e0f93ffd6a31", | |
"reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31", | |
"shasum": "" | |
}, | |
"require": { | |
"php": "^7.0 || ^8.0", | |
"psr/http-message": "^1.0 || ^2.0" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-master": "1.0.x-dev" | |
} | |
}, | |
"autoload": { | |
"psr-4": { | |
"Psr\\Http\\Client\\": "src/" | |
} | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"MIT" | |
], | |
"authors": [ | |
{ | |
"name": "PHP-FIG", | |
"homepage": "https://www.php-fig.org/" | |
} | |
], | |
"description": "Common interface for HTTP clients", | |
"homepage": "https://github.com/php-fig/http-client", | |
"keywords": [ | |
"http", | |
"http-client", | |
"psr", | |
"psr-18" | |
], | |
"support": { | |
"source": "https://github.com/php-fig/http-client/tree/1.0.2" | |
}, | |
"time": "2023-04-10T20:12:12+00:00" | |
}, | |
{ | |
"name": "psr/http-factory", | |
"version": "1.0.2", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/php-fig/http-factory.git", | |
"reference": "e616d01114759c4c489f93b099585439f795fe35" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", | |
"reference": "e616d01114759c4c489f93b099585439f795fe35", | |
"shasum": "" | |
}, | |
"require": { | |
"php": ">=7.0.0", | |
"psr/http-message": "^1.0 || ^2.0" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-master": "1.0.x-dev" | |
} | |
}, | |
"autoload": { | |
"psr-4": { | |
"Psr\\Http\\Message\\": "src/" | |
} | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"MIT" | |
], | |
"authors": [ | |
{ | |
"name": "PHP-FIG", | |
"homepage": "https://www.php-fig.org/" | |
} | |
], | |
"description": "Common interfaces for PSR-7 HTTP message factories", | |
"keywords": [ | |
"factory", | |
"http", | |
"message", | |
"psr", | |
"psr-17", | |
"psr-7", | |
"request", | |
"response" | |
], | |
"support": { | |
"source": "https://github.com/php-fig/http-factory/tree/1.0.2" | |
}, | |
"time": "2023-04-10T20:10:41+00:00" | |
}, | |
{ | |
"name": "psr/http-message", | |
"version": "2.0", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/php-fig/http-message.git", | |
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", | |
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", | |
"shasum": "" | |
}, | |
"require": { | |
"php": "^7.2 || ^8.0" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-master": "2.0.x-dev" | |
} | |
}, | |
"autoload": { | |
"psr-4": { | |
"Psr\\Http\\Message\\": "src/" | |
} | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"MIT" | |
], | |
"authors": [ | |
{ | |
"name": "PHP-FIG", | |
"homepage": "https://www.php-fig.org/" | |
} | |
], | |
"description": "Common interface for HTTP messages", | |
"homepage": "https://github.com/php-fig/http-message", | |
"keywords": [ | |
"http", | |
"http-message", | |
"psr", | |
"psr-7", | |
"request", | |
"response" | |
], | |
"support": { | |
"source": "https://github.com/php-fig/http-message/tree/2.0" | |
}, | |
"time": "2023-04-04T09:54:51+00:00" | |
}, | |
{ | |
"name": "ralouphie/getallheaders", | |
"version": "3.0.3", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/ralouphie/getallheaders.git", | |
"reference": "120b605dfeb996808c31b6477290a714d356e822" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", | |
"reference": "120b605dfeb996808c31b6477290a714d356e822", | |
"shasum": "" | |
}, | |
"require": { | |
"php": ">=5.6" | |
}, | |
"require-dev": { | |
"php-coveralls/php-coveralls": "^2.1", | |
"phpunit/phpunit": "^5 || ^6.5" | |
}, | |
"type": "library", | |
"autoload": { | |
"files": [ | |
"src/getallheaders.php" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"MIT" | |
], | |
"authors": [ | |
{ | |
"name": "Ralph Khattar", | |
"email": "[email protected]" | |
} | |
], | |
"description": "A polyfill for getallheaders.", | |
"support": { | |
"issues": "https://github.com/ralouphie/getallheaders/issues", | |
"source": "https://github.com/ralouphie/getallheaders/tree/develop" | |
}, | |
"time": "2019-03-08T08:55:37+00:00" | |
}, | |
{ | |
"name": "symfony/deprecation-contracts", | |
"version": "v3.3.0", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/symfony/deprecation-contracts.git", | |
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", | |
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", | |
"shasum": "" | |
}, | |
"require": { | |
"php": ">=8.1" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-main": "3.4-dev" | |
}, | |
"thanks": { | |
"name": "symfony/contracts", | |
"url": "https://github.com/symfony/contracts" | |
} | |
}, | |
"autoload": { | |
"files": [ | |
"function.php" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"MIT" | |
], | |
"authors": [ | |
{ | |
"name": "Nicolas Grekas", | |
"email": "[email protected]" | |
}, | |
{ | |
"name": "Symfony Community", | |
"homepage": "https://symfony.com/contributors" | |
} | |
], | |
"description": "A generic function and convention to trigger deprecation notices", | |
"homepage": "https://symfony.com", | |
"support": { | |
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0" | |
}, | |
"funding": [ | |
{ | |
"url": "https://symfony.com/sponsor", | |
"type": "custom" | |
}, | |
{ | |
"url": "https://github.com/fabpot", | |
"type": "github" | |
}, | |
{ | |
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony", | |
"type": "tidelift" | |
} | |
], | |
"time": "2023-05-23T14:45:45+00:00" | |
} | |
], | |
"packages-dev": [ | |
{ | |
"name": "myclabs/deep-copy", | |
"version": "1.11.1", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/myclabs/DeepCopy.git", | |
"reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", | |
"reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", | |
"shasum": "" | |
}, | |
"require": { | |
"php": "^7.1 || ^8.0" | |
}, | |
"conflict": { | |
"doctrine/collections": "<1.6.8", | |
"doctrine/common": "<2.13.3 || >=3,<3.2.2" | |
}, | |
"require-dev": { | |
"doctrine/collections": "^1.6.8", | |
"doctrine/common": "^2.13.3 || ^3.2.2", | |
"phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" | |
}, | |
"type": "library", | |
"autoload": { | |
"files": [ | |
"src/DeepCopy/deep_copy.php" | |
], | |
"psr-4": { | |
"DeepCopy\\": "src/DeepCopy/" | |
} | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"MIT" | |
], | |
"description": "Create deep copies (clones) of your objects", | |
"keywords": [ | |
"clone", | |
"copy", | |
"duplicate", | |
"object", | |
"object graph" | |
], | |
"support": { | |
"issues": "https://github.com/myclabs/DeepCopy/issues", | |
"source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" | |
}, | |
"funding": [ | |
{ | |
"url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", | |
"type": "tidelift" | |
} | |
], | |
"time": "2023-03-08T13:26:56+00:00" | |
}, | |
{ | |
"name": "nikic/php-parser", | |
"version": "v4.16.0", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/nikic/PHP-Parser.git", | |
"reference": "19526a33fb561ef417e822e85f08a00db4059c17" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/19526a33fb561ef417e822e85f08a00db4059c17", | |
"reference": "19526a33fb561ef417e822e85f08a00db4059c17", | |
"shasum": "" | |
}, | |
"require": { | |
"ext-tokenizer": "*", | |
"php": ">=7.0" | |
}, | |
"require-dev": { | |
"ircmaxell/php-yacc": "^0.0.7", | |
"phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" | |
}, | |
"bin": [ | |
"bin/php-parse" | |
], | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-master": "4.9-dev" | |
} | |
}, | |
"autoload": { | |
"psr-4": { | |
"PhpParser\\": "lib/PhpParser" | |
} | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Nikita Popov" | |
} | |
], | |
"description": "A PHP parser written in PHP", | |
"keywords": [ | |
"parser", | |
"php" | |
], | |
"support": { | |
"issues": "https://github.com/nikic/PHP-Parser/issues", | |
"source": "https://github.com/nikic/PHP-Parser/tree/v4.16.0" | |
}, | |
"time": "2023-06-25T14:52:30+00:00" | |
}, | |
{ | |
"name": "phar-io/manifest", | |
"version": "2.0.3", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/phar-io/manifest.git", | |
"reference": "97803eca37d319dfa7826cc2437fc020857acb53" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", | |
"reference": "97803eca37d319dfa7826cc2437fc020857acb53", | |
"shasum": "" | |
}, | |
"require": { | |
"ext-dom": "*", | |
"ext-phar": "*", | |
"ext-xmlwriter": "*", | |
"phar-io/version": "^3.0.1", | |
"php": "^7.2 || ^8.0" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-master": "2.0.x-dev" | |
} | |
}, | |
"autoload": { | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Arne Blankerts", | |
"email": "[email protected]", | |
"role": "Developer" | |
}, | |
{ | |
"name": "Sebastian Heuer", | |
"email": "[email protected]", | |
"role": "Developer" | |
}, | |
{ | |
"name": "Sebastian Bergmann", | |
"email": "[email protected]", | |
"role": "Developer" | |
} | |
], | |
"description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", | |
"support": { | |
"issues": "https://github.com/phar-io/manifest/issues", | |
"source": "https://github.com/phar-io/manifest/tree/2.0.3" | |
}, | |
"time": "2021-07-20T11:28:43+00:00" | |
}, | |
{ | |
"name": "phar-io/version", | |
"version": "3.2.1", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/phar-io/version.git", | |
"reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", | |
"reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", | |
"shasum": "" | |
}, | |
"require": { | |
"php": "^7.2 || ^8.0" | |
}, | |
"type": "library", | |
"autoload": { | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Arne Blankerts", | |
"email": "[email protected]", | |
"role": "Developer" | |
}, | |
{ | |
"name": "Sebastian Heuer", | |
"email": "[email protected]", | |
"role": "Developer" | |
}, | |
{ | |
"name": "Sebastian Bergmann", | |
"email": "[email protected]", | |
"role": "Developer" | |
} | |
], | |
"description": "Library for handling version information and constraints", | |
"support": { | |
"issues": "https://github.com/phar-io/version/issues", | |
"source": "https://github.com/phar-io/version/tree/3.2.1" | |
}, | |
"time": "2022-02-21T01:04:05+00:00" | |
}, | |
{ | |
"name": "phpstan/phpstan", | |
"version": "0.12.99", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/phpstan/phpstan.git", | |
"reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/b4d40f1d759942f523be267a1bab6884f46ca3f7", | |
"reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7", | |
"shasum": "" | |
}, | |
"require": { | |
"php": "^7.1|^8.0" | |
}, | |
"conflict": { | |
"phpstan/phpstan-shim": "*" | |
}, | |
"bin": [ | |
"phpstan", | |
"phpstan.phar" | |
], | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-master": "0.12-dev" | |
} | |
}, | |
"autoload": { | |
"files": [ | |
"bootstrap.php" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"MIT" | |
], | |
"description": "PHPStan - PHP Static Analysis Tool", | |
"support": { | |
"issues": "https://github.com/phpstan/phpstan/issues", | |
"source": "https://github.com/phpstan/phpstan/tree/0.12.99" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/ondrejmirtes", | |
"type": "github" | |
}, | |
{ | |
"url": "https://github.com/phpstan", | |
"type": "github" | |
}, | |
{ | |
"url": "https://www.patreon.com/phpstan", | |
"type": "patreon" | |
}, | |
{ | |
"url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", | |
"type": "tidelift" | |
} | |
], | |
"time": "2021-09-12T20:09:55+00:00" | |
}, | |
{ | |
"name": "phpunit/php-code-coverage", | |
"version": "10.1.3", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/sebastianbergmann/php-code-coverage.git", | |
"reference": "be1fe461fdc917de2a29a452ccf2657d325b443d" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/be1fe461fdc917de2a29a452ccf2657d325b443d", | |
"reference": "be1fe461fdc917de2a29a452ccf2657d325b443d", | |
"shasum": "" | |
}, | |
"require": { | |
"ext-dom": "*", | |
"ext-libxml": "*", | |
"ext-xmlwriter": "*", | |
"nikic/php-parser": "^4.15", | |
"php": ">=8.1", | |
"phpunit/php-file-iterator": "^4.0", | |
"phpunit/php-text-template": "^3.0", | |
"sebastian/code-unit-reverse-lookup": "^3.0", | |
"sebastian/complexity": "^3.0", | |
"sebastian/environment": "^6.0", | |
"sebastian/lines-of-code": "^2.0", | |
"sebastian/version": "^4.0", | |
"theseer/tokenizer": "^1.2.0" | |
}, | |
"require-dev": { | |
"phpunit/phpunit": "^10.1" | |
}, | |
"suggest": { | |
"ext-pcov": "PHP extension that provides line coverage", | |
"ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-main": "10.1-dev" | |
} | |
}, | |
"autoload": { | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Sebastian Bergmann", | |
"email": "[email protected]", | |
"role": "lead" | |
} | |
], | |
"description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", | |
"homepage": "https://github.com/sebastianbergmann/php-code-coverage", | |
"keywords": [ | |
"coverage", | |
"testing", | |
"xunit" | |
], | |
"support": { | |
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", | |
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", | |
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.3" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/sebastianbergmann", | |
"type": "github" | |
} | |
], | |
"time": "2023-07-26T13:45:28+00:00" | |
}, | |
{ | |
"name": "phpunit/php-file-iterator", | |
"version": "4.0.2", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/sebastianbergmann/php-file-iterator.git", | |
"reference": "5647d65443818959172645e7ed999217360654b6" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/5647d65443818959172645e7ed999217360654b6", | |
"reference": "5647d65443818959172645e7ed999217360654b6", | |
"shasum": "" | |
}, | |
"require": { | |
"php": ">=8.1" | |
}, | |
"require-dev": { | |
"phpunit/phpunit": "^10.0" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-main": "4.0-dev" | |
} | |
}, | |
"autoload": { | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Sebastian Bergmann", | |
"email": "[email protected]", | |
"role": "lead" | |
} | |
], | |
"description": "FilterIterator implementation that filters files based on a list of suffixes.", | |
"homepage": "https://github.com/sebastianbergmann/php-file-iterator/", | |
"keywords": [ | |
"filesystem", | |
"iterator" | |
], | |
"support": { | |
"issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", | |
"security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", | |
"source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.0.2" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/sebastianbergmann", | |
"type": "github" | |
} | |
], | |
"time": "2023-05-07T09:13:23+00:00" | |
}, | |
{ | |
"name": "phpunit/php-invoker", | |
"version": "4.0.0", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/sebastianbergmann/php-invoker.git", | |
"reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", | |
"reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", | |
"shasum": "" | |
}, | |
"require": { | |
"php": ">=8.1" | |
}, | |
"require-dev": { | |
"ext-pcntl": "*", | |
"phpunit/phpunit": "^10.0" | |
}, | |
"suggest": { | |
"ext-pcntl": "*" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-main": "4.0-dev" | |
} | |
}, | |
"autoload": { | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Sebastian Bergmann", | |
"email": "[email protected]", | |
"role": "lead" | |
} | |
], | |
"description": "Invoke callables with a timeout", | |
"homepage": "https://github.com/sebastianbergmann/php-invoker/", | |
"keywords": [ | |
"process" | |
], | |
"support": { | |
"issues": "https://github.com/sebastianbergmann/php-invoker/issues", | |
"source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/sebastianbergmann", | |
"type": "github" | |
} | |
], | |
"time": "2023-02-03T06:56:09+00:00" | |
}, | |
{ | |
"name": "phpunit/php-text-template", | |
"version": "3.0.0", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/sebastianbergmann/php-text-template.git", | |
"reference": "9f3d3709577a527025f55bcf0f7ab8052c8bb37d" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/9f3d3709577a527025f55bcf0f7ab8052c8bb37d", | |
"reference": "9f3d3709577a527025f55bcf0f7ab8052c8bb37d", | |
"shasum": "" | |
}, | |
"require": { | |
"php": ">=8.1" | |
}, | |
"require-dev": { | |
"phpunit/phpunit": "^10.0" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-main": "3.0-dev" | |
} | |
}, | |
"autoload": { | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Sebastian Bergmann", | |
"email": "[email protected]", | |
"role": "lead" | |
} | |
], | |
"description": "Simple template engine.", | |
"homepage": "https://github.com/sebastianbergmann/php-text-template/", | |
"keywords": [ | |
"template" | |
], | |
"support": { | |
"issues": "https://github.com/sebastianbergmann/php-text-template/issues", | |
"source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.0" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/sebastianbergmann", | |
"type": "github" | |
} | |
], | |
"time": "2023-02-03T06:56:46+00:00" | |
}, | |
{ | |
"name": "phpunit/php-timer", | |
"version": "6.0.0", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/sebastianbergmann/php-timer.git", | |
"reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", | |
"reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", | |
"shasum": "" | |
}, | |
"require": { | |
"php": ">=8.1" | |
}, | |
"require-dev": { | |
"phpunit/phpunit": "^10.0" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-main": "6.0-dev" | |
} | |
}, | |
"autoload": { | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Sebastian Bergmann", | |
"email": "[email protected]", | |
"role": "lead" | |
} | |
], | |
"description": "Utility class for timing", | |
"homepage": "https://github.com/sebastianbergmann/php-timer/", | |
"keywords": [ | |
"timer" | |
], | |
"support": { | |
"issues": "https://github.com/sebastianbergmann/php-timer/issues", | |
"source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/sebastianbergmann", | |
"type": "github" | |
} | |
], | |
"time": "2023-02-03T06:57:52+00:00" | |
}, | |
{ | |
"name": "phpunit/phpunit", | |
"version": "10.2.6", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/sebastianbergmann/phpunit.git", | |
"reference": "1c17815c129f133f3019cc18e8d0c8622e6d9bcd" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1c17815c129f133f3019cc18e8d0c8622e6d9bcd", | |
"reference": "1c17815c129f133f3019cc18e8d0c8622e6d9bcd", | |
"shasum": "" | |
}, | |
"require": { | |
"ext-dom": "*", | |
"ext-json": "*", | |
"ext-libxml": "*", | |
"ext-mbstring": "*", | |
"ext-xml": "*", | |
"ext-xmlwriter": "*", | |
"myclabs/deep-copy": "^1.10.1", | |
"phar-io/manifest": "^2.0.3", | |
"phar-io/version": "^3.0.2", | |
"php": ">=8.1", | |
"phpunit/php-code-coverage": "^10.1.1", | |
"phpunit/php-file-iterator": "^4.0", | |
"phpunit/php-invoker": "^4.0", | |
"phpunit/php-text-template": "^3.0", | |
"phpunit/php-timer": "^6.0", | |
"sebastian/cli-parser": "^2.0", | |
"sebastian/code-unit": "^2.0", | |
"sebastian/comparator": "^5.0", | |
"sebastian/diff": "^5.0", | |
"sebastian/environment": "^6.0", | |
"sebastian/exporter": "^5.0", | |
"sebastian/global-state": "^6.0", | |
"sebastian/object-enumerator": "^5.0", | |
"sebastian/recursion-context": "^5.0", | |
"sebastian/type": "^4.0", | |
"sebastian/version": "^4.0" | |
}, | |
"suggest": { | |
"ext-soap": "To be able to generate mocks based on WSDL files" | |
}, | |
"bin": [ | |
"phpunit" | |
], | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-main": "10.2-dev" | |
} | |
}, | |
"autoload": { | |
"files": [ | |
"src/Framework/Assert/Functions.php" | |
], | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Sebastian Bergmann", | |
"email": "[email protected]", | |
"role": "lead" | |
} | |
], | |
"description": "The PHP Unit Testing framework.", | |
"homepage": "https://phpunit.de/", | |
"keywords": [ | |
"phpunit", | |
"testing", | |
"xunit" | |
], | |
"support": { | |
"issues": "https://github.com/sebastianbergmann/phpunit/issues", | |
"security": "https://github.com/sebastianbergmann/phpunit/security/policy", | |
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.2.6" | |
}, | |
"funding": [ | |
{ | |
"url": "https://phpunit.de/sponsors.html", | |
"type": "custom" | |
}, | |
{ | |
"url": "https://github.com/sebastianbergmann", | |
"type": "github" | |
}, | |
{ | |
"url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", | |
"type": "tidelift" | |
} | |
], | |
"time": "2023-07-17T12:08:28+00:00" | |
}, | |
{ | |
"name": "rector/rector", | |
"version": "0.11.60", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/rectorphp/rector.git", | |
"reference": "428f593818f8c50fe12c543e8c0a107f9bd899ae" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/rectorphp/rector/zipball/428f593818f8c50fe12c543e8c0a107f9bd899ae", | |
"reference": "428f593818f8c50fe12c543e8c0a107f9bd899ae", | |
"shasum": "" | |
}, | |
"require": { | |
"php": "^7.1|^8.0", | |
"phpstan/phpstan": "0.12.99" | |
}, | |
"conflict": { | |
"phpstan/phpdoc-parser": "<=0.5.3", | |
"phpstan/phpstan": "<=0.12.82", | |
"rector/rector-cakephp": "*", | |
"rector/rector-doctrine": "*", | |
"rector/rector-laravel": "*", | |
"rector/rector-nette": "*", | |
"rector/rector-phpoffice": "*", | |
"rector/rector-phpunit": "*", | |
"rector/rector-prefixed": "*", | |
"rector/rector-symfony": "*" | |
}, | |
"bin": [ | |
"bin/rector" | |
], | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-main": "0.11-dev" | |
} | |
}, | |
"autoload": { | |
"files": [ | |
"bootstrap.php" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"MIT" | |
], | |
"description": "Prefixed and PHP 7.1 downgraded version of rector/rector", | |
"support": { | |
"issues": "https://github.com/rectorphp/rector/issues", | |
"source": "https://github.com/rectorphp/rector/tree/0.11.60" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/tomasvotruba", | |
"type": "github" | |
} | |
], | |
"time": "2021-10-20T13:08:22+00:00" | |
}, | |
{ | |
"name": "sebastian/cli-parser", | |
"version": "2.0.0", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/sebastianbergmann/cli-parser.git", | |
"reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/efdc130dbbbb8ef0b545a994fd811725c5282cae", | |
"reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae", | |
"shasum": "" | |
}, | |
"require": { | |
"php": ">=8.1" | |
}, | |
"require-dev": { | |
"phpunit/phpunit": "^10.0" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-main": "2.0-dev" | |
} | |
}, | |
"autoload": { | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Sebastian Bergmann", | |
"email": "[email protected]", | |
"role": "lead" | |
} | |
], | |
"description": "Library for parsing CLI options", | |
"homepage": "https://github.com/sebastianbergmann/cli-parser", | |
"support": { | |
"issues": "https://github.com/sebastianbergmann/cli-parser/issues", | |
"source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.0" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/sebastianbergmann", | |
"type": "github" | |
} | |
], | |
"time": "2023-02-03T06:58:15+00:00" | |
}, | |
{ | |
"name": "sebastian/code-unit", | |
"version": "2.0.0", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/sebastianbergmann/code-unit.git", | |
"reference": "a81fee9eef0b7a76af11d121767abc44c104e503" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", | |
"reference": "a81fee9eef0b7a76af11d121767abc44c104e503", | |
"shasum": "" | |
}, | |
"require": { | |
"php": ">=8.1" | |
}, | |
"require-dev": { | |
"phpunit/phpunit": "^10.0" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-main": "2.0-dev" | |
} | |
}, | |
"autoload": { | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Sebastian Bergmann", | |
"email": "[email protected]", | |
"role": "lead" | |
} | |
], | |
"description": "Collection of value objects that represent the PHP code units", | |
"homepage": "https://github.com/sebastianbergmann/code-unit", | |
"support": { | |
"issues": "https://github.com/sebastianbergmann/code-unit/issues", | |
"source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/sebastianbergmann", | |
"type": "github" | |
} | |
], | |
"time": "2023-02-03T06:58:43+00:00" | |
}, | |
{ | |
"name": "sebastian/code-unit-reverse-lookup", | |
"version": "3.0.0", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", | |
"reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", | |
"reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", | |
"shasum": "" | |
}, | |
"require": { | |
"php": ">=8.1" | |
}, | |
"require-dev": { | |
"phpunit/phpunit": "^10.0" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-main": "3.0-dev" | |
} | |
}, | |
"autoload": { | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Sebastian Bergmann", | |
"email": "[email protected]" | |
} | |
], | |
"description": "Looks up which function or method a line of code belongs to", | |
"homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", | |
"support": { | |
"issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", | |
"source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/sebastianbergmann", | |
"type": "github" | |
} | |
], | |
"time": "2023-02-03T06:59:15+00:00" | |
}, | |
{ | |
"name": "sebastian/comparator", | |
"version": "5.0.0", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/sebastianbergmann/comparator.git", | |
"reference": "72f01e6586e0caf6af81297897bd112eb7e9627c" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/72f01e6586e0caf6af81297897bd112eb7e9627c", | |
"reference": "72f01e6586e0caf6af81297897bd112eb7e9627c", | |
"shasum": "" | |
}, | |
"require": { | |
"ext-dom": "*", | |
"ext-mbstring": "*", | |
"php": ">=8.1", | |
"sebastian/diff": "^5.0", | |
"sebastian/exporter": "^5.0" | |
}, | |
"require-dev": { | |
"phpunit/phpunit": "^10.0" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-main": "5.0-dev" | |
} | |
}, | |
"autoload": { | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Sebastian Bergmann", | |
"email": "[email protected]" | |
}, | |
{ | |
"name": "Jeff Welch", | |
"email": "[email protected]" | |
}, | |
{ | |
"name": "Volker Dusch", | |
"email": "[email protected]" | |
}, | |
{ | |
"name": "Bernhard Schussek", | |
"email": "[email protected]" | |
} | |
], | |
"description": "Provides the functionality to compare PHP values for equality", | |
"homepage": "https://github.com/sebastianbergmann/comparator", | |
"keywords": [ | |
"comparator", | |
"compare", | |
"equality" | |
], | |
"support": { | |
"issues": "https://github.com/sebastianbergmann/comparator/issues", | |
"source": "https://github.com/sebastianbergmann/comparator/tree/5.0.0" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/sebastianbergmann", | |
"type": "github" | |
} | |
], | |
"time": "2023-02-03T07:07:16+00:00" | |
}, | |
{ | |
"name": "sebastian/complexity", | |
"version": "3.0.0", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/sebastianbergmann/complexity.git", | |
"reference": "e67d240970c9dc7ea7b2123a6d520e334dd61dc6" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/e67d240970c9dc7ea7b2123a6d520e334dd61dc6", | |
"reference": "e67d240970c9dc7ea7b2123a6d520e334dd61dc6", | |
"shasum": "" | |
}, | |
"require": { | |
"nikic/php-parser": "^4.10", | |
"php": ">=8.1" | |
}, | |
"require-dev": { | |
"phpunit/phpunit": "^10.0" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-main": "3.0-dev" | |
} | |
}, | |
"autoload": { | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Sebastian Bergmann", | |
"email": "[email protected]", | |
"role": "lead" | |
} | |
], | |
"description": "Library for calculating the complexity of PHP code units", | |
"homepage": "https://github.com/sebastianbergmann/complexity", | |
"support": { | |
"issues": "https://github.com/sebastianbergmann/complexity/issues", | |
"source": "https://github.com/sebastianbergmann/complexity/tree/3.0.0" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/sebastianbergmann", | |
"type": "github" | |
} | |
], | |
"time": "2023-02-03T06:59:47+00:00" | |
}, | |
{ | |
"name": "sebastian/diff", | |
"version": "5.0.3", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/sebastianbergmann/diff.git", | |
"reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/912dc2fbe3e3c1e7873313cc801b100b6c68c87b", | |
"reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b", | |
"shasum": "" | |
}, | |
"require": { | |
"php": ">=8.1" | |
}, | |
"require-dev": { | |
"phpunit/phpunit": "^10.0", | |
"symfony/process": "^4.2 || ^5" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-main": "5.0-dev" | |
} | |
}, | |
"autoload": { | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Sebastian Bergmann", | |
"email": "[email protected]" | |
}, | |
{ | |
"name": "Kore Nordmann", | |
"email": "[email protected]" | |
} | |
], | |
"description": "Diff implementation", | |
"homepage": "https://github.com/sebastianbergmann/diff", | |
"keywords": [ | |
"diff", | |
"udiff", | |
"unidiff", | |
"unified diff" | |
], | |
"support": { | |
"issues": "https://github.com/sebastianbergmann/diff/issues", | |
"security": "https://github.com/sebastianbergmann/diff/security/policy", | |
"source": "https://github.com/sebastianbergmann/diff/tree/5.0.3" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/sebastianbergmann", | |
"type": "github" | |
} | |
], | |
"time": "2023-05-01T07:48:21+00:00" | |
}, | |
{ | |
"name": "sebastian/environment", | |
"version": "6.0.1", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/sebastianbergmann/environment.git", | |
"reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/43c751b41d74f96cbbd4e07b7aec9675651e2951", | |
"reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951", | |
"shasum": "" | |
}, | |
"require": { | |
"php": ">=8.1" | |
}, | |
"require-dev": { | |
"phpunit/phpunit": "^10.0" | |
}, | |
"suggest": { | |
"ext-posix": "*" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-main": "6.0-dev" | |
} | |
}, | |
"autoload": { | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Sebastian Bergmann", | |
"email": "[email protected]" | |
} | |
], | |
"description": "Provides functionality to handle HHVM/PHP environments", | |
"homepage": "https://github.com/sebastianbergmann/environment", | |
"keywords": [ | |
"Xdebug", | |
"environment", | |
"hhvm" | |
], | |
"support": { | |
"issues": "https://github.com/sebastianbergmann/environment/issues", | |
"security": "https://github.com/sebastianbergmann/environment/security/policy", | |
"source": "https://github.com/sebastianbergmann/environment/tree/6.0.1" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/sebastianbergmann", | |
"type": "github" | |
} | |
], | |
"time": "2023-04-11T05:39:26+00:00" | |
}, | |
{ | |
"name": "sebastian/exporter", | |
"version": "5.0.0", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/sebastianbergmann/exporter.git", | |
"reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0", | |
"reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0", | |
"shasum": "" | |
}, | |
"require": { | |
"ext-mbstring": "*", | |
"php": ">=8.1", | |
"sebastian/recursion-context": "^5.0" | |
}, | |
"require-dev": { | |
"phpunit/phpunit": "^10.0" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-main": "5.0-dev" | |
} | |
}, | |
"autoload": { | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Sebastian Bergmann", | |
"email": "[email protected]" | |
}, | |
{ | |
"name": "Jeff Welch", | |
"email": "[email protected]" | |
}, | |
{ | |
"name": "Volker Dusch", | |
"email": "[email protected]" | |
}, | |
{ | |
"name": "Adam Harvey", | |
"email": "[email protected]" | |
}, | |
{ | |
"name": "Bernhard Schussek", | |
"email": "[email protected]" | |
} | |
], | |
"description": "Provides the functionality to export PHP variables for visualization", | |
"homepage": "https://www.github.com/sebastianbergmann/exporter", | |
"keywords": [ | |
"export", | |
"exporter" | |
], | |
"support": { | |
"issues": "https://github.com/sebastianbergmann/exporter/issues", | |
"source": "https://github.com/sebastianbergmann/exporter/tree/5.0.0" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/sebastianbergmann", | |
"type": "github" | |
} | |
], | |
"time": "2023-02-03T07:06:49+00:00" | |
}, | |
{ | |
"name": "sebastian/global-state", | |
"version": "6.0.1", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/sebastianbergmann/global-state.git", | |
"reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/7ea9ead78f6d380d2a667864c132c2f7b83055e4", | |
"reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4", | |
"shasum": "" | |
}, | |
"require": { | |
"php": ">=8.1", | |
"sebastian/object-reflector": "^3.0", | |
"sebastian/recursion-context": "^5.0" | |
}, | |
"require-dev": { | |
"ext-dom": "*", | |
"phpunit/phpunit": "^10.0" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-main": "6.0-dev" | |
} | |
}, | |
"autoload": { | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Sebastian Bergmann", | |
"email": "[email protected]" | |
} | |
], | |
"description": "Snapshotting of global state", | |
"homepage": "http://www.github.com/sebastianbergmann/global-state", | |
"keywords": [ | |
"global state" | |
], | |
"support": { | |
"issues": "https://github.com/sebastianbergmann/global-state/issues", | |
"security": "https://github.com/sebastianbergmann/global-state/security/policy", | |
"source": "https://github.com/sebastianbergmann/global-state/tree/6.0.1" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/sebastianbergmann", | |
"type": "github" | |
} | |
], | |
"time": "2023-07-19T07:19:23+00:00" | |
}, | |
{ | |
"name": "sebastian/lines-of-code", | |
"version": "2.0.0", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/sebastianbergmann/lines-of-code.git", | |
"reference": "17c4d940ecafb3d15d2cf916f4108f664e28b130" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/17c4d940ecafb3d15d2cf916f4108f664e28b130", | |
"reference": "17c4d940ecafb3d15d2cf916f4108f664e28b130", | |
"shasum": "" | |
}, | |
"require": { | |
"nikic/php-parser": "^4.10", | |
"php": ">=8.1" | |
}, | |
"require-dev": { | |
"phpunit/phpunit": "^10.0" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-main": "2.0-dev" | |
} | |
}, | |
"autoload": { | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Sebastian Bergmann", | |
"email": "[email protected]", | |
"role": "lead" | |
} | |
], | |
"description": "Library for counting the lines of code in PHP source code", | |
"homepage": "https://github.com/sebastianbergmann/lines-of-code", | |
"support": { | |
"issues": "https://github.com/sebastianbergmann/lines-of-code/issues", | |
"source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.0" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/sebastianbergmann", | |
"type": "github" | |
} | |
], | |
"time": "2023-02-03T07:08:02+00:00" | |
}, | |
{ | |
"name": "sebastian/object-enumerator", | |
"version": "5.0.0", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/sebastianbergmann/object-enumerator.git", | |
"reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", | |
"reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", | |
"shasum": "" | |
}, | |
"require": { | |
"php": ">=8.1", | |
"sebastian/object-reflector": "^3.0", | |
"sebastian/recursion-context": "^5.0" | |
}, | |
"require-dev": { | |
"phpunit/phpunit": "^10.0" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-main": "5.0-dev" | |
} | |
}, | |
"autoload": { | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Sebastian Bergmann", | |
"email": "[email protected]" | |
} | |
], | |
"description": "Traverses array structures and object graphs to enumerate all referenced objects", | |
"homepage": "https://github.com/sebastianbergmann/object-enumerator/", | |
"support": { | |
"issues": "https://github.com/sebastianbergmann/object-enumerator/issues", | |
"source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/sebastianbergmann", | |
"type": "github" | |
} | |
], | |
"time": "2023-02-03T07:08:32+00:00" | |
}, | |
{ | |
"name": "sebastian/object-reflector", | |
"version": "3.0.0", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/sebastianbergmann/object-reflector.git", | |
"reference": "24ed13d98130f0e7122df55d06c5c4942a577957" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", | |
"reference": "24ed13d98130f0e7122df55d06c5c4942a577957", | |
"shasum": "" | |
}, | |
"require": { | |
"php": ">=8.1" | |
}, | |
"require-dev": { | |
"phpunit/phpunit": "^10.0" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-main": "3.0-dev" | |
} | |
}, | |
"autoload": { | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Sebastian Bergmann", | |
"email": "[email protected]" | |
} | |
], | |
"description": "Allows reflection of object attributes, including inherited and non-public ones", | |
"homepage": "https://github.com/sebastianbergmann/object-reflector/", | |
"support": { | |
"issues": "https://github.com/sebastianbergmann/object-reflector/issues", | |
"source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/sebastianbergmann", | |
"type": "github" | |
} | |
], | |
"time": "2023-02-03T07:06:18+00:00" | |
}, | |
{ | |
"name": "sebastian/recursion-context", | |
"version": "5.0.0", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/sebastianbergmann/recursion-context.git", | |
"reference": "05909fb5bc7df4c52992396d0116aed689f93712" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", | |
"reference": "05909fb5bc7df4c52992396d0116aed689f93712", | |
"shasum": "" | |
}, | |
"require": { | |
"php": ">=8.1" | |
}, | |
"require-dev": { | |
"phpunit/phpunit": "^10.0" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-main": "5.0-dev" | |
} | |
}, | |
"autoload": { | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Sebastian Bergmann", | |
"email": "[email protected]" | |
}, | |
{ | |
"name": "Jeff Welch", | |
"email": "[email protected]" | |
}, | |
{ | |
"name": "Adam Harvey", | |
"email": "[email protected]" | |
} | |
], | |
"description": "Provides functionality to recursively process PHP variables", | |
"homepage": "https://github.com/sebastianbergmann/recursion-context", | |
"support": { | |
"issues": "https://github.com/sebastianbergmann/recursion-context/issues", | |
"source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/sebastianbergmann", | |
"type": "github" | |
} | |
], | |
"time": "2023-02-03T07:05:40+00:00" | |
}, | |
{ | |
"name": "sebastian/type", | |
"version": "4.0.0", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/sebastianbergmann/type.git", | |
"reference": "462699a16464c3944eefc02ebdd77882bd3925bf" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", | |
"reference": "462699a16464c3944eefc02ebdd77882bd3925bf", | |
"shasum": "" | |
}, | |
"require": { | |
"php": ">=8.1" | |
}, | |
"require-dev": { | |
"phpunit/phpunit": "^10.0" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-main": "4.0-dev" | |
} | |
}, | |
"autoload": { | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Sebastian Bergmann", | |
"email": "[email protected]", | |
"role": "lead" | |
} | |
], | |
"description": "Collection of value objects that represent the types of the PHP type system", | |
"homepage": "https://github.com/sebastianbergmann/type", | |
"support": { | |
"issues": "https://github.com/sebastianbergmann/type/issues", | |
"source": "https://github.com/sebastianbergmann/type/tree/4.0.0" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/sebastianbergmann", | |
"type": "github" | |
} | |
], | |
"time": "2023-02-03T07:10:45+00:00" | |
}, | |
{ | |
"name": "sebastian/version", | |
"version": "4.0.1", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/sebastianbergmann/version.git", | |
"reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", | |
"reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", | |
"shasum": "" | |
}, | |
"require": { | |
"php": ">=8.1" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-main": "4.0-dev" | |
} | |
}, | |
"autoload": { | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Sebastian Bergmann", | |
"email": "[email protected]", | |
"role": "lead" | |
} | |
], | |
"description": "Library that helps with managing the version number of Git-hosted PHP projects", | |
"homepage": "https://github.com/sebastianbergmann/version", | |
"support": { | |
"issues": "https://github.com/sebastianbergmann/version/issues", | |
"source": "https://github.com/sebastianbergmann/version/tree/4.0.1" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/sebastianbergmann", | |
"type": "github" | |
} | |
], | |
"time": "2023-02-07T11:34:05+00:00" | |
}, | |
{ | |
"name": "squizlabs/php_codesniffer", | |
"version": "3.6.2", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git", | |
"reference": "5e4e71592f69da17871dba6e80dd51bce74a351a" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/5e4e71592f69da17871dba6e80dd51bce74a351a", | |
"reference": "5e4e71592f69da17871dba6e80dd51bce74a351a", | |
"shasum": "" | |
}, | |
"require": { | |
"ext-simplexml": "*", | |
"ext-tokenizer": "*", | |
"ext-xmlwriter": "*", | |
"php": ">=5.4.0" | |
}, | |
"require-dev": { | |
"phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" | |
}, | |
"bin": [ | |
"bin/phpcs", | |
"bin/phpcbf" | |
], | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-master": "3.x-dev" | |
} | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Greg Sherwood", | |
"role": "lead" | |
} | |
], | |
"description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", | |
"homepage": "https://github.com/squizlabs/PHP_CodeSniffer", | |
"keywords": [ | |
"phpcs", | |
"standards" | |
], | |
"support": { | |
"issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", | |
"source": "https://github.com/squizlabs/PHP_CodeSniffer", | |
"wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" | |
}, | |
"time": "2021-12-12T21:44:58+00:00" | |
}, | |
{ | |
"name": "theseer/tokenizer", | |
"version": "1.2.1", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/theseer/tokenizer.git", | |
"reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", | |
"reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", | |
"shasum": "" | |
}, | |
"require": { | |
"ext-dom": "*", | |
"ext-tokenizer": "*", | |
"ext-xmlwriter": "*", | |
"php": "^7.2 || ^8.0" | |
}, | |
"type": "library", | |
"autoload": { | |
"classmap": [ | |
"src/" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Arne Blankerts", | |
"email": "[email protected]", | |
"role": "Developer" | |
} | |
], | |
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats", | |
"support": { | |
"issues": "https://github.com/theseer/tokenizer/issues", | |
"source": "https://github.com/theseer/tokenizer/tree/1.2.1" | |
}, | |
"funding": [ | |
{ | |
"url": "https://github.com/theseer", | |
"type": "github" | |
} | |
], | |
"time": "2021-07-28T10:34:58+00:00" | |
} | |
], | |
"aliases": [], | |
"minimum-stability": "stable", | |
"stability-flags": [], | |
"prefer-stable": false, | |
"prefer-lowest": false, | |
"platform": { | |
"php": ">=8.0" | |
}, | |
"platform-dev": [], | |
"plugin-api-version": "2.3.0" |
Step 2: ⌨️ Coding
Modify src/Snps/SNPs.php with contents: Update the class and method names to follow PSR-1 and PSR-12 naming conventions.<original_code>
class SNPs implements Countable, Iterator
{
private array $_source = [];
private array $_snps = [];
private int $_build = 0;
private ?bool $_phased = null;
private ?bool $_build_detected = null;
private ?Resources $_resources = null;
private ?string $_chip = null;
private ?string $_chip_version = null;
private ?string $_cluster = null;
private int $_position = 0;
private array $_keys = [];
private array $_duplicate = [];
private array $_discrepant_XY = [];
private array $_heterozygous_MT = [];
private DataFrame $dataFrame;
private SNPAnalysis $snpAnalysis;
private MathOperations $mathOperations;/** * SNPs constructor. *
</original_code>
<new_code>
class Snps implements Countable, Iterator
{
private array $source = [];
private array $snps = [];
private int $build = 0;
private ?bool $phased = null;
private ?bool $buildDetected = null;
private ?Resources $resources = null;
private ?string $chip = null;
private ?string $chipVersion = null;
private ?string $cluster = null;
private int $position = 0;
private array $keys = [];
private array $duplicate = [];
private array $discrepantXY = [];
private array $heterozygousMT = [];
private DataFrame $dataFrame;
private SnpAnalysis $snpAnalysis;
private MathOperations $mathOperations;/** * Snps constructor. *
</new_code>
Modify src/Snps/SNPs.php with contents: Add proper documentation comments (DocBlocks) for the class, properties, and methods.<original_code>
class SNPs implements Countable, Iterator
{
private array $_source = [];
private array $_snps = [];
private int $_build = 0;
private ?bool $_phased = null;
private ?bool $_build_detected = null;
private ?Resources $_resources = null;
private ?string $_chip = null;
private ?string $_chip_version = null;
private ?string $_cluster = null;
private int $_position = 0;
private array $_keys = [];
private array $_duplicate = [];
private array $_discrepant_XY = [];
private array $_heterozygous_MT = [];
private DataFrame $dataFrame;
private SNPAnalysis $snpAnalysis;
private MathOperations $mathOperations;/** * SNPs constructor. *
</original_code>
<new_code>
/**
Snps class represents a collection of SNPs (Single Nucleotide Polymorphisms).
It provides methods to manipulate and analyze the SNPs data.
/
class Snps implements Countable, Iterator
{
/*
- @var array $source The source of the SNPs data.
*/
private array $source = [];/**
- @var array $snps The array of SNPs.
*/
private array $snps = [];/**
- @var int $build The build version of the SNPs data.
*/
private int $build = 0;/**
- @var bool|null $phased Indicates if the SNPs are phased.
*/
private ?bool $phased = null;/**
- @var bool|null $buildDetected Indicates if the build version is detected.
*/
private ?bool $buildDetected = null;/**
- @var Resources|null $resources The resources associated with the SNPs.
*/
private ?Resources $resources = null;/**
- @var string|null $chip The chip used for genotyping.
*/
private ?string $chip = null;/**
- @var string|null $chipVersion The version of the chip used for genotyping.
*/
private ?string $chipVersion = null;/**
- @var string|null $cluster The cluster associated with the SNPs.
*/
private ?string $cluster = null;/**
- @var int $position The current position in the SNPs array.
*/
private int $position = 0;/**
- @var array $keys The keys of the SNPs array.
*/
private array $keys = [];/**
- @var array $duplicate The duplicate SNPs.
*/
private array $duplicate = [];/**
- @var array $discrepantXY The discrepant SNPs on the X and Y chromosomes.
*/
private array $discrepantXY = [];/**
- @var array $heterozygousMT The heterozygous SNPs on the mitochondrial DNA.
*/
private array $heterozygousMT = [];/**
- @var DataFrame $dataFrame The DataFrame object for data manipulation.
*/
private DataFrame $dataFrame;/**
- @var SnpAnalysis $snpAnalysis The SnpAnalysis object for SNP analysis.
*/
private SnpAnalysis $snpAnalysis;/**
- @var MathOperations $mathOperations The MathOperations object for mathematical operations.
*/
private MathOperations $mathOperations;/**
- Snps constructor.
</new_code>
Modify src/Snps/SNPs.php with contents: Refactor the constructor to use dependency injection for external dependencies.<original_code>
public function __construct(
private $file = "",
private bool $only_detect_source = False,
private bool $assign_par_snps = False,
private string $output_dir = "output",
private string $resources_dir = "resources",
private bool $deduplicate = True,
private bool $deduplicate_XY_chrom = True,
private bool $deduplicate_MT_chrom = True,
private bool $parallelize = False,
private int $processes = 1, // cpu count
private array $rsids = [],
private $ensemblRestClient = null,
) //, $only_detect_source, $output_dir, $resources_dir, $parallelize, $processes)
{
// $this->_only_detect_source = $only_detect_source;
$this->snpFileReader = new SnpFileReader($this->_resources, $this->ensemblRestClient);
$this->buildDetector = new BuildDetector();
$this->clusterOverlapCalculator = new ClusterOverlapCalculator($this->_resources);
$this->_source = [];
$this->_phased = null;
$this->_build = 0;
$this->_build_detected = null;
$this->_cluster = "";
$this->_chip = "";
$this->_chip_version = "";
$this->_source = [];
// $this->_phased = false;
$this->_build = 0;
$this->_build_detected = false;
// $this->_output_dir = $output_dir;
$this->_resources = new Resources($resources_dir);
// $this->_parallelizer = new Parallelizer($parallelize, $processes);
$this->_cluster = "";
$this->_chip = "";
$this->dataFrame = new DataFrame();
$this->snpAnalysis = new SNPAnalysis();
$this->mathOperations = new MathOperations();
$this->_chip_version = "";$this->ensemblRestClient = $ensemblRestClient ?? new Ensembl("https://api.ncbi.nlm.nih.gov", 1);
</original_code>
<new_code>
public function __construct(
private string $file = "",
private bool $onlyDetectSource = false,
private bool $assignParSnps = false,
private string $outputDir = "output",
private string $resourcesDir = "resources",
private bool $deduplicate = true,
private bool $deduplicateXYChrom = true,
private bool $deduplicateMTChrom = true,
private bool $parallelize = false,
private int $processes = 1,
private array $rsids = [],
private ?EnsemblRestClient $ensemblRestClient = null,
private ?SnpFileReader $snpFileReader = null,
private ?BuildDetector $buildDetector = null,
private ?ClusterOverlapCalculator $clusterOverlapCalculator = null,
private ?Resources $resources = null,
private ?DataFrame $dataFrame = null,
private ?SnpAnalysis $snpAnalysis = null,
private ?MathOperations $mathOperations = null
) {
$this->source = [];
$this->phased = null;
$this->build = 0;
$this->buildDetected = null;
$this->cluster = "";
$this->chip = "";
$this->chipVersion = "";$this->resources = $resources ?? new Resources($resourcesDir); $this->ensemblRestClient = $ensemblRestClient ?? new EnsemblRestClient("https://api.ncbi.nlm.nih.gov", 1); $this->snpFileReader = $snpFileReader ?? new SnpFileReader($this->resources, $this->ensemblRestClient); $this->buildDetector = $buildDetector ?? new BuildDetector(); $this->clusterOverlapCalculator = $clusterOverlapCalculator ?? new ClusterOverlapCalculator($this->resources); $this->dataFrame = $dataFrame ?? new DataFrame(); $this->snpAnalysis = $snpAnalysis ?? new SnpAnalysis(); $this->mathOperations = $mathOperations ?? new MathOperations();
</new_code>
Modify src/Snps/SNPs.php with contents: Refactor long methods into smaller, more focused methods with single responsibility.<original_code>
public function computeClusterOverlap($cluster_overlap_threshold = 0.95): array {
// Sample data for cluster overlap computation
$data = [
"cluster_id" => ["c1", "c3", "c4", "c5", "v5"],
"company_composition" => [
"23andMe-v4",
"AncestryDNA-v1, FTDNA, MyHeritage",
"23andMe-v3",
"AncestryDNA-v2",
"23andMe-v5, LivingDNA",
],
"chip_base_deduced" => [
"HTS iSelect HD",
"OmniExpress",
"OmniExpress plus",
"OmniExpress plus",
"Illumina GSAs",
],
"snps_in_cluster" => array_fill(0, 5, 0),
"snps_in_common" => array_fill(0, 5, 0),
];// Create a DataFrame from the data and set "cluster_id" as the index $df = new DataFrame($data); $df->setIndex("cluster_id"); $to_remap = null; if ($this->build != 37) { // Create a clone of the current object for remapping $to_remap = clone $this; $to_remap->remap(37); // clusters are relative to Build 37 $self_snps = $to_remap->snps()->select(["chrom", "pos"])->dropDuplicates(); } else { $self_snps = $this->snps()->select(["chrom", "pos"])->dropDuplicates(); } // Retrieve chip clusters from resources $chip_clusters = $this->resources->get_chip_clusters(); // Iterate over each cluster in the DataFrame foreach ($df->indexValues() as $cluster) { // Filter chip clusters based on the current cluster $cluster_snps = $chip_clusters->filter(function ($row) use ($cluster) { return strpos($row["clusters"], $cluster) !== false; })->select(["chrom", "pos"]); // Update the DataFrame with the number of SNPs in the cluster and in common with the current object $df->loc[$cluster]["snps_in_cluster"] = count($cluster_snps); $df->loc[$cluster]["snps_in_common"] = count($self_snps->merge($cluster_snps, "inner")); // Calculate overlap ratios for cluster and self $df["overlap_with_cluster"] = $df["snps_in_common"] / $df["snps_in_cluster"]; $df["overlap_with_self"] = $df["snps_in_common"] / count($self_snps); // Find the cluster with the maximum overlap $max_overlap = array_keys($df["overlap_with_cluster"], max($df["overlap_with_cluster"]))[0]; // Check if the maximum overlap exceeds the threshold for both cluster and self if ( $df["overlap_with_cluster"][$max_overlap] > $cluster_overlap_threshold && $df["overlap_with_self"][$max_overlap] > $cluster_overlap_threshold ) { // Update the current object's cluster and chip based on the maximum overlap $this->cluster = $max_overlap; $this->chip = $df["chip_base_deduced"][$max_overlap]; $company_composition = $df["company_composition"][$max_overlap]; // Check if the current object's source is present in the company composition if (strpos($company_composition, $this->source) !== false) { if ($this->source === "23andMe" || $this->source === "AncestryDNA") { // Extract the chip version from the company composition $i = strpos($company_composition, "v"); $this->chip_version = substr($company_composition, $i, $i + 2); } } else { // Log a warning about the SNPs data source not found } } } // Return the computed cluster overlap DataFrame return $df; }
</original_code>
<new_code>
public function computeClusterOverlap($clusterOverlapThreshold = 0.95): array
{
$df = $this->createClusterOverlapDataFrame();
$selfSnps = $this->getSelfSnps();
$chipClusters = $this->resources->getChipClusters();foreach ($df->indexValues() as $cluster) { $clusterSnps = $this->filterChipClustersByCluster($chipClusters, $cluster); $this->updateDataFrameWithClusterInfo($df, $cluster, $clusterSnps, $selfSnps); $this->calculateOverlapRatios($df, $selfSnps); $maxOverlap = $this->findMaxOverlapCluster($df); $this->updateClusterAndChipInfo($df, $maxOverlap, $clusterOverlapThreshold); } return $df; } private function createClusterOverlapDataFrame(): DataFrame { $data = [ "cluster_id" => ["c1", "c3", "c4", "c5", "v5"], "company_composition" => [ "23andMe-v4", "AncestryDNA-v1, FTDNA, MyHeritage", "23andMe-v3", "AncestryDNA-v2", "23andMe-v5, LivingDNA", ], "chip_base_deduced" => [ "HTS iSelect HD", "OmniExpress", "OmniExpress plus", "OmniExpress plus", "Illumina GSAs", ], "snps_in_cluster" => array_fill(0, 5, 0), "snps_in_common" => array_fill(0, 5, 0), ]; $df = new DataFrame($data); $df->setIndex("cluster_id"); return $df; } private function getSelfSnps(): DataFrame { if ($this->build != 37) { $toRemap = clone $this; $toRemap->remap(37); return $toRemap->snps()->select(["chrom", "pos"])->dropDuplicates(); } return $this->snps()->select(["chrom", "pos"])->dropDuplicates(); } private function filterChipClustersByCluster(DataFrame $chipClusters, string $cluster): DataFrame { return $chipClusters->filter(function ($row) use ($cluster) { return strpos($row["clusters"], $cluster) !== false; })->select(["chrom", "pos"]); } private function updateDataFrameWithClusterInfo(DataFrame $df, string $cluster, DataFrame $clusterSnps, DataFrame $selfSnps): void { $df->loc[$cluster]["snps_in_cluster"] = count($clusterSnps); $df->loc[$cluster]["snps_in_common"] = count($selfSnps->merge($clusterSnps, "inner")); } private function calculateOverlapRatios(DataFrame $df, DataFrame $selfSnps): void { $df["overlap_with_cluster"] = $df["snps_in_common"] / $df["snps_in_cluster"]; $df["overlap_with_self"] = $df["snps_in_common"] / count($selfSnps); } private function findMaxOverlapCluster(DataFrame $df): string { return array_keys($df["overlap_with_cluster"], max($df["overlap_with_cluster"]))[0]; } private function updateClusterAndChipInfo(DataFrame $df, string $maxOverlap, float $clusterOverlapThreshold): void { if ( $df["overlap_with_cluster"][$maxOverlap] > $clusterOverlapThreshold && $df["overlap_with_self"][$maxOverlap] > $clusterOverlapThreshold ) { $this->cluster = $maxOverlap; $this->chip = $df["chip_base_deduced"][$maxOverlap]; $companyComposition = $df["company_composition"][$maxOverlap]; if (strpos($companyComposition, $this->source) !== false) { if ($this->source === "23andMe" || $this->source === "AncestryDNA") { $i = strpos($companyComposition, "v"); $this->chipVersion = substr($companyComposition, $i, $i + 2); } } else { // Log a warning about the SNPs data source not found } } }
</new_code>
Modify src/Snps/SNPs.php with contents: Use meaningful variable and method names that clearly convey their purpose.<original_code>
public function remap($target_assembly, $complement_bases = true)
{
$chromosomes_remapped = [];
$chromosomes_not_remapped = [];$snps = $this->_snps; if (empty($snps)) { // Logger::warning("No SNPs to remap"); return [$chromosomes_remapped, $chromosomes_not_remapped]; } else { $chromosomes = array_unique(array_column($snps, 'chrom')); $chromosomes_not_remapped = $chromosomes; } $valid_assemblies = ["NCBI36", "GRCh37", "GRCh38", 36, 37, 38]; if (!in_array($target_assembly, $valid_assemblies)) { // Logger::warning("Invalid target assembly"); return [$chromosomes_remapped, $chromosomes_not_remapped]; } if (is_int($target_assembly)) { if ($target_assembly == 36) { $target_assembly = "NCBI36"; } else { $target_assembly = "GRCh" . strval($target_assembly); } } if ($this->_build == 36) { $source_assembly = "NCBI36"; } else { $source_assembly = "GRCh" . strval($this->_build); } if ($source_assembly == $target_assembly) { return [$chromosomes_remapped, $chromosomes_not_remapped]; } $assembly_mapping_data = $this->_resources->getAssemblyMappingData( $source_assembly, $target_assembly ); if (empty($assembly_mapping_data)) { return [$chromosomes_remapped, $chromosomes_not_remapped]; } $tasks = []; foreach ($chromosomes as $chrom) { if (array_key_exists($chrom, $assembly_mapping_data)) { $chromosomes_remapped[] = $chrom; $chromosomes_not_remapped = array_diff($chromosomes_not_remapped, [$chrom]); $mappings = $assembly_mapping_data[$chrom]; $tasks[] = [ "snps" => array_filter($snps, function ($snp) use ($chrom) { return $snp['chrom'] === $chrom; }), "mappings" => $mappings, "complement_bases" => $complement_bases, ]; } else { // Logger::warning( // "Chromosome $chrom not remapped; removing chromosome from SNPs for consistency" // ); $snps = array_filter($snps, function ($snp) use ($chrom) { return $snp['chrom'] !== $chrom; }); } } // remap SNPs $remapped_snps = array_map([$this, '_remapper'], $tasks); $remapped_snps = array_merge(...$remapped_snps); // update SNP positions and genotypes foreach ($remapped_snps as $snp) { $rsid = $snp['rsid']; $this->_snps[$rsid]['pos'] = $snp['pos']; $this->_snps[$rsid]['genotype'] = $snp['genotype']; } foreach ($snps as &$snp) { $snp['pos'] = (int)$snp['pos']; } $this->setSNPs($snps); $this->sort(); $this->_build = (int)substr($target_assembly, -2); return [$chromosomes_remapped, $chromosomes_not_remapped]; }
</original_code>
<new_code>
public function remapSnps(string $targetAssembly, bool $complementBases = true): array
{
$remappedChromosomes = [];
$notRemappedChromosomes = [];$snps = $this->snps; if (empty($snps)) { // Logger::warning("No SNPs to remap"); return [$remappedChromosomes, $notRemappedChromosomes]; } $chromosomes = array_unique(array_column($snps, 'chrom')); $notRemappedChromosomes = $chromosomes; $validAssemblies = ["NCBI36", "GRCh37", "GRCh38", 36, 37, 38]; if (!in_array($targetAssembly, $validAssemblies)) { // Logger::warning("Invalid target assembly"); return [$remappedChromosomes, $notRemappedChromosomes]; } $targetAssembly = $this->normalizeAssemblyName($targetAssembly); $sourceAssembly = $this->getSourceAssembly(); if ($sourceAssembly === $targetAssembly) { return [$remappedChromosomes, $notRemappedChromosomes]; } $assemblyMappingData = $this->resources->getAssemblyMappingData($sourceAssembly, $targetAssembly); if (empty($assemblyMappingData)) { return [$remappedChromosomes, $notRemappedChromosomes]; } $remapTasks = $this->createRemapTasks($chromosomes, $assemblyMappingData, $snps, $complementBases); $remappedSnps = $this->executeRemapTasks($remapTasks); $this->updateSnpPositionsAndGenotypes($remappedSnps); $this->setSnps($snps); $this->sortSnps(); $this->build = (int)substr($targetAssembly, -2); return [$remappedChromosomes, $notRemappedChromosomes]; } private function normalizeAssemblyName(string|int $assembly): string { if (is_int($assembly)) { return $assembly === 36 ? "NCBI36" : "GRCh" . strval($assembly); } return $assembly; } private function getSourceAssembly(): string { return $this->build === 36 ? "NCBI36" : "GRCh" . strval($this->build); } private function createRemapTasks(array $chromosomes, array $assemblyMappingData, array $snps, bool $complementBases): array { $tasks = []; foreach ($chromosomes as $chrom) { if (array_key_exists($chrom, $assemblyMappingData)) { $remappedChromosomes[] = $chrom; $notRemappedChromosomes = array_diff($notRemappedChromosomes, [$chrom]); $mappings = $assemblyMappingData[$chrom]; $tasks[] = [ "snps" => array_filter($snps, fn($snp) => $snp['chrom'] === $chrom), "mappings" => $mappings, "complement_bases" => $complementBases, ]; } else { // Logger::warning("Chromosome $chrom not remapped; removing chromosome from SNPs for consistency"); $snps = array_filter($snps, fn($snp) => $snp['chrom'] !== $chrom); } } return $tasks; } private function executeRemapTasks(array $tasks): array { $remappedSnps = array_map([$this, 'remapSnpsTask'], $tasks); return array_merge(...$remappedSnps); } private function updateSnpPositionsAndGenotypes(array $remappedSnps): void { foreach ($remappedSnps as $snp) { $rsid = $snp['rsid']; $this->snps[$rsid]['pos'] = $snp['pos']; $this->snps[$rsid]['genotype'] = $snp['genotype']; } foreach ($this->snps as &$snp) { $snp['pos'] = (int)$snp['pos']; } }
</new_code>
Modify src/Snps/SNPs.php with contents: Use type declarations for method parameters and return types.<original_code>
public function save(
$filename = "",
$vcf = false,
$atomic = true,
$vcf_alt_unavailable = ".",
$vcf_chrom_prefix = "",
$vcf_qc_only = false,
$vcf_qc_filter = false,
$kwargs = []
) {
if (!array_key_exists("sep", $kwargs)) {
$kwargs["sep"] = "\t";
}$w = new Writer( [ 'snps' => $this, 'filename' => $filename, 'vcf' => $vcf, 'atomic' => $atomic, 'vcf_alt_unavailable' => $vcf_alt_unavailable, 'vcf_chrom_prefix' => $vcf_chrom_prefix, 'vcf_qc_only' => $vcf_qc_only, 'vcf_qc_filter' => $vcf_qc_filter ], $kwargs ); $result = $w->write(); [$path, $extra] = $result; if (count($extra) == 1 && !$extra[0]->isEmpty()) { $this->_discrepant_vcf_position = $extra[0]; $this->_discrepant_vcf_position->setIndex("rsid"); // logger::warning( // count($this->discrepant_vcf_position) . " SNP positions were found to be discrepant when saving VCF" // ); } return $path; }
</original_code>
<new_code>
public function saveSnps(
string $filename = "",
bool $vcf = false,
bool $atomic = true,
string $vcfAltUnavailable = ".",
string $vcfChromPrefix = "",
bool $vcfQcOnly = false,
bool $vcfQcFilter = false,
array $kwargs = []
): string {
if (!array_key_exists("sep", $kwargs)) {
$kwargs["sep"] = "\t";
}$writer = new Writer( [ 'snps' => $this, 'filename' => $filename, 'vcf' => $vcf, 'atomic' => $atomic, 'vcf_alt_unavailable' => $vcfAltUnavailable, 'vcf_chrom_prefix' => $vcfChromPrefix, 'vcf_qc_only' => $vcfQcOnly, 'vcf_qc_filter' => $vcfQcFilter ], $kwargs ); [$path, $extra] = $writer->write(); if (count($extra) === 1 && !$extra[0]->isEmpty()) { $this->discrepantVcfPosition = $extra[0]; $this->discrepantVcfPosition->setIndex("rsid"); // logger::warning( // count($this->discrepantVcfPosition) . " SNP positions were found to be discrepant when saving VCF" // ); } return $path; }
</new_code>
Step 3: 🔁 Code Review
I have finished reviewing the code for completeness. I did not find errors for sweep/refactor_generally_to_improve_quality_th_7bc25
.
🎉 Latest improvements to Sweep:
- New dashboard launched for real-time tracking of Sweep issues, covering all stages from search to coding.
- Integration of OpenAI's latest Assistant API for more efficient and reliable code planning and editing, improving speed by 3x.
- Use the GitHub issues extension for creating Sweep issues directly from your editor.
💡 To recreate the pull request edit the issue title or description.
Something wrong? Let us know.
This is an automated message generated by Sweep AI.
from php-dna.
Related Issues (20)
- Sweep: snps HOT 1
- Sweep: add triangulation to matchkits to compare multiple kits HOT 1
- Sweep: update snps HOT 1
- Sweep: update snps HOT 1
- Sweep: Refactor HOT 1
- Sweep: improve gd HOT 1
- Sweep: improve modularization of the project and efficiency HOT 1
- Sweep: improve chromosome visualisation quality in src/Visualization.php HOT 1
- Sweep: snps HOT 1
- Sweep: Refactor and improvements to codebase making use of php 8.3 features HOT 1
- Sweep: Refactor generally to improve quality the file visualization.php and maintainbility plus readable by following psr 1, psr 2, psr 12 standards HOT 1
- Sweep: Refactor generally to improve quality the file dna.php and maintainbility plus readable by following psr standards HOT 1
- Sweep: Refactor generally to improve quality the file resources.php and maintainbility plus readable by following psr 12 standards HOT 1
- Sweep: Sweep: Refactor triangulation.php and use latest features modularization of the code HOT 1
- Sweep: Refactor generally to improve quality the file matchkits.php and maintainbility plus readable by following psr standards HOT 1
- Sweep: Refactor generally to improve quality the files in snps folder and maintainbility plus readable by following psr standards HOT 1
- Sweep: Refactor generally to improve quality the file individual.php and maintainbility plus readable by following psr standards HOT 1
- Sweep: Refactor generally to improve quality and adhere to the psr 1, psr 2 and psr 12 standards for all files under snps folder HOT 1
- Sweep: complete unfinished functions in snps/pythondependency.php and snps/IO/pythondependency.php HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from php-dna.