# GenPerl Tutorial

- Introduction
- Overview of GenPerl Objects
- Overview of the GenPerl API
- The GenPerl Database
- Using GenPerl

# Introduction

GenPerl consists of a collection of OO Perl modules and an API for interacting with GenPerl objects. Functionality currently implemented in the GenPerl API is mainly related to managing the persistence of GenPerl objects in a relational database. This includes simple create, read, update and delete operations as well as more complex querying functionality. There is also a small amount of analysis functionality implemented including the formatting of files for analysis by commonly used genetic analysis software.

GenPerl is designed to facilitate the development of Perl scripts for applications related to genetic analysis. As such, it does not include ready to use programs or friendly graphical user interfaces (GUIs). Instead, GenPerl provides reusable Perl modules that facilitate writing Perl scripts for the storage, formatting and analysis of a wide range of genetic data. GenPerl enables the development applications which can process and analyze large quantities of data in customized ways that are typically difficult or impossible with large GUI-based systems.

The original version of GenPerl was developed in the Research Department of Genomica Corp. where it was used to produce research protytypes and to manage and analyze data related to research projects with which Genomica was involved.

GenPerl should be considered alpha software.

### License

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

### Feedback

Please send all feedback, comments, etc. to:

Steve Mathias
Genomica Corporation
1745 38th Street
Boulder, CO 80301

# Overview of GenPerl Objects

The following convention is observed in the naming of the attributes and relationships of genperl objects:

- Attributes with names beginning with a lower case letter have simple scalar values.
- Attributes with names beginning with an upper case letter have values which are pointers to more complicated data structures.
- Attributes with names that are singular have zero or one value.
- Attributes with names that are plural may have multiple values.

The tables below detail the internal structure of the GenPerl object. When creating and updating objects, there are many instances where it is necessary to reference other objects. This is done in a general way via IDs or importIDs. IDs can only be used to reference objects that have already been saved to the database, and have thus been given a database ID. Otherwise, importIDs must be used. Basically, if you want to reference an object that already exists in the database, use ids; if you are making references to objects that are being imported together as part of the same import session, use importIDs.

Internally, all object references are anonymous hashes with the following key/value structure:

{ name => String
id/importID => Integer/String }

The name is optional and is allowed for convenience. Either an id or an importID is required and identifies the referenced object. See the methods in Genetics::API::DB::Insert for details on how importIDs are saved and used.

Keep in mind that genperl objects may be instantiated and used without necessarily being stored in a genperl database. Therefore, Genetics::Object and its subclasses do not enforce the data type and format constraints required if the objects are to be saved to the genperl schema via the API. Any additional format or size requirements of the API are noted parenthetically in the tables below.

Genetics::Object is the abstract superclass for all GenPerl objects.

The GenPerl objects with which one interacts directly are:

- Cluster representing groupings of objects, by reference.
- DNASample representing laboratory samples of DNA.
- FrequencySource representing observed allele and/or haplotype frequencies.
- Genotype representing the observed allele(s) an individual has at a polymorphic locus. In GenPerl terms, this is the alleles a given Subject has for a given Marker or SNP.
- Haplotype representing a specific combination of alleles known or predicted to be segregating together.
- HtMarkerCollection representing a set of Markers/SNPs from which Haplotypes can be constructed.
- Kindred representing groups of Subjects related genetically or by mariage.
- Map representing an ordered set of chromosomal loci.
- Marker representing polymorphic genetic loci.
- Phenotype representing the observable properties - genetic and/orenvironmental - of an individual. In GenPerl terms, this is the value a given Subject has for a given StudyVariable.
- SNP representing single nucleotide polymorphisms.
- StudyVariable representing definitions of physical traits, affection status, environmental exposure, drug treatments, etc.
- Subject representing the individuals being studied.
- TissueSample representing laboratory tissue samples.

## Genetics::Object

Required Attributes
Name | Data type and Format | Description
--- | --- | ---
name | String (<= 120 characters) | The name of the object.
id | Integer | A database generated id. This should only be used for updating and deleting objetcs
OR
importID | String | An identifier that should be unique within an import session. These get stored in the database as keywords.
Optional Attributes
Name | Data type and Format | Description
dateCreated | String (``YYYY-MM-DD'') |
dateModified | String (``YYYY-MM-DD'') | This field is maintained by the database and is ignored on import and update.
url | String (< 65638 bytes) |
comment | String (< 65638 bytes) |
NameAliases | Array pointer to a list of hash pointers. The referenced hashes should have the following key/value structure: name => String (<= 120 characters) comment => String (< 120 characters) |
Contact | Hash pointer. The referenced hash should have the following key/value structure: name => String (<= 120 characters) organization => String (<= 120 characters) comment => String (< 65638 bytes) |
DBXReferences | Array pointer to a list of hash pointers. The referenced hashes should have the following key/value structure: accessionNumber => String (<= 32 characters) databaseName => String (<= 32 characters) schemaName => String (<= 120 characters) comment => String (< 65638 bytes) |
Keywords | Array pointer to a list of hash pointers. The referenced hashes should have the following key/value structure: name => String (<= 32 characters) dataType => String (<= 32 characters) value => String (<= 32 characters) description => String (<= 255 characters) |

## Genetics::Object::Cluster

Required Attributes
Name | Data type and Format | Description
--- | --- | ---
clusterType | String ("Mixed", "Subject", "Kindred", "Marker", "SNP", "Genotype", "StudyVariable", "Phenotype", "HaplotypeMarkerCollection", "Haplotype", "Map", "FrequencySource", "DNASample", or "TissueSample") | The type of object the Cluster references.
Optional Attributes
Name | Data type and Format | Description
Contents | Array pointer to a list of object references. | These are the references to the Cluster's contents. Be careful when making references by importID to objects already saved to the database. This will likely have unpredictable results unless all the ImportID Keywords in the database are unique.

Example:

    $cluster = new Genetics::Cluster(name => 'All Subjects',
    				 importID => 271,
    				 dateCreated => $today,
    				 clusterType => "Subject",
    				 comment => "All Subjects imported from DMs Study JX",
    				 Keywords => [ {name => "Test Data",
    						dataType => "Boolean",
    						value => 1},
    					     ],
    				 Contents=> [ {name => "JXPed1-1", importID => 12},
    					      {name => "JXPed1-2", importID => 32},
    					      {name => "JXPed1-3", importID => 22},
    					      {name => "JXPed2-1", importID => 42},
    					      {name => "JXPed2-2", importID => 43},
    					      {name => "JXPed2-3", importID => 44},
    					    ],
    				) ;

## Genetics::Object::DNASample

Optional Attributes
Name | Data type and Format | Description
--- | --- | ---
dateCollected | String ("YYYY-MM-DD") | Date the sample was collected/prepared.
amount | Float (6,3) |
amountUnits | String ("g", "mg", "ug", or "ng") |
concentration | Float (6,3) |
concUnits | String ("mg/ml", "ug/ml", "ug/ul", or "ng/ul") |
Subject | Subject reference | Reference to the Subject from which the Sample is derived.
Genotypes | Array pointer to a list of Genotype references | Reference(s) to Genotype(s) derived from the Sample.

Example:

    $sample = new Genetics::DNASample(name => 'SM20.1-3',
    				  importID => 272,
    				  dateCreated => $today,
    				  comment => "Third attempt to get DNA from this Sample",
    				  Keywords => [ {name => "Test Data",
    						 dataType => "Boolean",
    						 value => 1},
    					      ],
    				  dateCollected => "2001-01-18",
    				  amount => 3.26,
    				  amountUnits => "mg",
    				  concentration => 1.1,
    				  concUnits => "mg/ml",
    				  Subject => {name => 'EAPed20.1',
    					      importID => 12},
    				  Genotypes => [ {name => '1-D12S91',
    						  importID => 13},
    						 {name => '1-EAEx1.1',
    						  importID => 14},
    					       ],
    				 ) ;

## Genetics::Object::FrequencySource

Optional Attributes
Name | Data type and Format | Description
--- | --- | ---
ObsAlleleFrequencies | Array pointer to a list of hash pointers. The referenced hashes should have the following key/value structure: Allele => { Marker => Marker Reference, name => String (<= 4 characters), type => String ("Code", "Size", "RepeatNumber", "Nucleotide", or "Undefined") } frequency => Float |
ObsHtFrequencies | Array pointer to a list of hash pointers. The referenced hashes should have the following key/value structure: Haplotype => Haplotype Reference frequency => Float |

Example:

    $fs = new Genetics::FrequencySource(name => 'WICGR SNP Freqs',
    				    importID => 270,
    				    dateCreated => $today,
    				    Keywords => [ {name => "Test Data",
    						   dataType => "Boolean",
    						   value => 1},
    						],
    				    ObsAlleleFrequencies => [ {Allele => {Marker => {name => "EAEx1.1",
    										     importID => 265},
    									  name => "T",
    									  type => "nucleotide"},
    							       frequency => "0.64",
    							      },
    							      {Allele => {Marker => {name => "EAEx1.1",
    										     importID => 265},
    									  name => "C",
    									  type => "nucleotide"},
    							       frequency => "0.36",
    							      }
    							    ],
    				    ObsHtFrequencies => [ {Haplotype => {name => '12pEA1.2',
    									 importID => 269,},
    							   frequency => 1.00,
    							  }
    							]
    				   ) ;

## Genetics::Object::Genotype

Required Attributes
Name | Data type and Format | Description
--- | --- | ---
isActive | Boolean (1 or 0) |
Subject | Subject Reference |
Marker | Marker Reference |
AlleleCalls | Array pointer to a list of hash pointers. The referenced hashes should have the following key/value structure: alleleName => String (<= 4 characters) alleleType => String ("Code", "Size", "RepeatNumber", "Nucleotide", or "Undefined") phase => String ("Unknown", "Maternal", or "Paternal") AssayAttrs => Array pointer to a list of AssayAttribute hash pointers. | The AssayAttributes in here apply to individual AlleleCalls.
Optional Attributes
Name | Data type and Format | Description
icResult | String ("Pass", "Fail", "Ambiguous", or "Unknown") | Inheritance check result.
dateCollected | String ("YYYY-MM-DD") |
Sample | DNASample Reference |
AssayAttrs | Array pointer to a list of AssayAttribute hash pointers. | These are AssayAttributes that apply to the Genotype as a whole.

Example:

    $gt = new Genetics::Genotype(name => '1-D12S91',
    			     importID => 13,
    			     dateCreated => $today,
    			     Keywords => [ {name => "Test Data",
    					    dataType => "Boolean",
    					    value => 1},
    					 ],
    			     isActive => 1,
    			     icResult => "Pass",
    			     dateCollected => "1993-11-13",
    			     Subject => {name => "EAPed20.1", importID => 12},
    			     Marker => {name => "D12S91", importID => 1},
    			     AssayAttrs => [ {name => "lab",
    					      dataType=> "string",
    					      value => "Lab 6"},
    					     {name => "machineID",
    					      dataType => "String",
    					      value => "ABC1234"}
    					   ],
    			     AlleleCalls => [ {alleleName => 3,
    					       alleleType => "Code",
    					       phase => "Maternal",
    					       AssayAttrs => [ {name => "peakHeight",
    								dataType => "Number",
    								value => 367} ]
    					      },
    					      {alleleName => 1,
    					       alleleType => "Code",
    					       phase => "Paternal",
    					       AssayAttrs => [ {name => "peakHeight",
    								dataType => "Number",
    								value => 435} ]
    					      },
    					    ]
    			    ) ;

## Genetics::Object::Haplotype

Required Attributes
Name | Data type and Format | Description
--- | --- | ---
MarkerCollection | HtMarkerCollection Reference |
Alleles | Array pointer to a list of hash pointers. The referenced hashes should have the following key/value structure: name => String (<= 4 characters) type => String ("Code", "Size", "RepeatNumber", "Nucleotide", or "Undefined") |

Example:

    $ht = new Genetics::Haplotype(name => '12pEA1.1',
    			      importID => 268,
    			      dateCreated => $today,
    			      Keywords => [ {name => "Test Data",
    					     dataType => "Boolean",
    					     value => 1},
    					  ],
    			      MarkerCollection => {name => "12pEA1", importID => 267},
    			      Alleles => [ {name => 2, type => "code"},
    					   {name => "C", type => "nucleotide"} ],
    			     ) ;

## Genetics::Object::HtMarkerCollection

Required Attributes
Name | Data type and Format | Description
--- | --- | ---
Markers | Array pointer to a list of hash pointers. The referenced hashes should have the following key/value structure: Marker => Marker References distToNext => Float (10,5) | The order of the Marker references should reflect the map order of the markers, and thus the corresponding allele order in the haplotypes derived from the marker collection.
Optional Attributes
Name | Data type and Format | Description
distanceUnits | String ("cM", "bp", "Kb", "Mb", "cR", "cR3000", "cR10000", or "Theta") |

Example:

    $hmc = new Genetics::HtMarkerCollection(name => '12pEA1',
    					importID => 267,
    					dateCreated => $today,
    					Keywords => [ {name => "Test Data",
    						       dataType => "Boolean",
    						       value => 1},
    						    ],
    					Markers => [ {Marker => {name => "D12S91",
                                                                     importID => 1},
    						      distToNext => "1.2"},
    						     {Marker => {name => "EAEx1.1",
    						                 importID => 265},
    						     }
    						   ],
    					distanceUnits => "cM",
    				       ) ;

## Genetics::Object::Kindred

Required Attributes
Name | Data type and Format | Description
--- | --- | ---
isDerived | Boolean (1 or 0) |
Optional Attributes
Name | Data type and Format | Description
Subjects | Array pointer to a list of Subject References |
DerivedFrom | Kindred Reference |

Example:

    $kindred = new Genetics::Kindred(name => 'JXPed2',
    				 importID => 45,
    				 dateCreated => $today,
    				 comment => "Litt et. al. (1994)",
    				 Keywords => [ {name => "Test Data",
    						dataType => "Boolean",
    						value => 1},
    					       {name => "Disease",
    						dataType => "String",
    						value => "Episodic Ataxia"} ],
    				 NameAliases => [ {name => "Ped20",
    						   contactName => "J.P. Morgan"}, ],
    				 Subjects => [ {name => "EAPed20.1", importID => 42},
    					       {name => "EAPed20.1000", importID => 43},
    					       {name => "EAPed20.1001", importID => 44},
    					     ],
    				) ;

## Genetics::Object::Map

Required Attributes
Name | Data type and Format | Description
--- | --- | ---
orderingMethod | String ("Relative" or "Global") | Default value is Relative.
distanceUnits | String ("cM", "bp", "Kb", "Mb", "cR", "cR3000", "cR10000", or "Theta") | Default value is cM
OrderedMapElements | Array pointer to a list of hash pointers. The referenced hashes should ahve the following key/value structure: Marker => Marker References distance => Float (10,5) | The order of the OME/Marker references should reflect the map order of the markers.

Example:

    $map = new Genetics::Map(name => 'Chr12 2 PO',
    			 importID => 121,
    			 dateCreated => $today,
    			 comment => "Stupid 2-marker map.",
    			 Keywords => [ {name => "Test Data",
    					dataType => "Boolean",
    					value => 1},
    				     ],
    			 chromosome => "12",
    			 orderingMethod => "Relative",
    			 distanceUnits => "cM",
    			 Organism => {genusSpecies => "Pongo pongo"},
    			 OrderedMapElements => [ {SeqObj => {name => "D12S91", importID => 1},
    						  distance => 1.3},
    						 {SeqObj => {name => "EAEx1.1", importID => 265}}
    					       ],
    			) ;

## Genetics::Object::Marker

Required Attributes
Name | Data type and Format | Description
--- | --- | ---
malePloidy | Integer (<= 99) | Default value = 2.
femalePloidy | Integer (<= 99) | Default value = 2.
Optional Attributes
Name | Data type and Format | Description
chromosome | String (<= 8 characters) |
polymorphismType | String () |
polymorphismIndex1 | Integer (<= 16777215) |
polymorphismIndex2 | Integer (<= 16777215) |
repeatSequence | String (<= 8 characters) |
Alleles | Array pointer to a list of hash pointers. The referenced hashes should have the following key/value structure: name => String (<= 4 characters) type => String ("Code", "Size", "RepeatNumber", "Nucleotide", or "Undefined") |
Sequence | Hash pointer. The referenced hash should have the following key/value structure: sequence => String (<= 65535 characters) length => Integer (<= 16777215) lengthUnits => String ("bp", "Kb", or "Mb") | This is the DNA sequence associated with the marker.
ISCNMapLocations | Array pointer to a list of hash pointers. The referenced hashes should have the following key/value structure: chrNumber => String (<= 8 characters) chrArm => String (<= 8 characters) band => String (<= 16 characters) bandingMethod => String (<= 32 characters) | These represent cytogenetic map locations associated with the marker.
Organism | Hash pointer. The referenced hash should have the following key/value structure: genusSpecies => String (<= 255 characters) subspecies => String (<= 120 characters) strain => String (<= 120 characters) |

Example:

    $marker = new Genetics::Marker(name => 'D12S91',
    			       importID => 1,
    			       dateCreated => $today,
    			       comment => "Marker in EA critical region",
    			       NameAliases => [ {name => "d12s6666"}
    					      ],
    			       Contact => {name => "Jean Weissenbach",
    					   organization => "Genethon"},
    			       DBXReferences => [ {accessionNumber => "NT_009758.3",
    						   databaseName => "GenBank"}
    						],
    			       Keywords => [ {name => "Test Data",
    					      dataType => "Boolean",
    					      value => 1},
    					   ],
    			       chromosome => "12",
    			       malePloidy => 2,
    			       femalePloidy => 2,
    			       polymorphismType => "Repeat",
    			       polymorphismIndex1 => 7,
    			       polymorphismIndex2 => 11,
    			       repeatSequence => "CA",
    			       Organism => {genusSpecies => "Homo sapiens"},
    			       Alleles => [ {name => 1, type => "Code"},
    					    {name => 2, type => "Code"},
    					    {name => 3, type => "Code"},
    					    {name => 4, type => "Code"},
    					    {name => 5, type => "Code"},
    					    {name => 6, type => "Code"} ],
    			       ISCNMapLocations => [ {chrNumber => "12",
    						      chrArm => "p",
    						      band => "12.2.1",
    						      bandingMethod => "Geimsa"}, ],
    			       Sequence => {lengthUnits => "bp",
    					    length => 17,
    					    sequence => "ACGTUMRCACAWSYKVHDBXN"},
    			      ) ;

## Genetics::Object::Phenotype

Required Attributes
Name | Data type and Format | Description
--- | --- | ---
value | Float (12,5) OR String ("YYYY-MM-DD") OR Integer (<= 99) | The datatype of the value depends on the format of the StudyVariable with which the Phenotype is associated.
isActive | Boolean (0 or 1) |
Subject | Subject Reference |
StudyVariable | StudyVariable Reference |
Optional Attributes
Name | Data type and Format | Description
dateCollected | String ("YYYY-MM-DD") |
AssayAttrs | Array pointer to a list of AssayAttribute hash pointers. |

Example:

    $pt = new Genetics::Phenotype(name => 'JXPed1-1-Age',
    			      importID => 266,
    			      dateCreated => $today,
    			      dateCollected => "1987-03-17",
    			      Keywords => [ {name => "Test Data",
    					    dataType => "Boolean",
    					    value => 1},
    					 ],
    			      Subject => {name => "JXPed1-1", importID => 12},
    			      StudyVariable => {name => "Age", importID => 444},
    			      AssayAttrs => [ {name => "Clinic Name",
    					       dataType => "String",
    					       value => "Sister of Gracious Mercy and Hope"},
    					    ],
    			      value => 12,
    			      isActive => 1,
    			     ) ;

## Genetics::Object::SNP

Required Attributes
Name | Data type and Format | Description
--- | --- | ---
snpType | String () |
functionClass | String () |
isConfirmed | Boolean (0 or 1) | Default value = 1.
malePloidy | Integer (<= 99) | Default value = 2.
femalePloidy | Integer (<= 99) | Default value = 2.
Optional Attributes
Name | Data type and Format | Description
chromosome | String (<= 8 characters) |
snpIndex | Integer (<= 16777215) |
confirmMethod | String (<= 255 characters) |
Alleles | Array pointer to a list of hash pointers. The referenced hashes should have the following key/value structure: name => String (<= 4 characters) type => String ("Code", "Size", "RepeatNumber", "Nucleotide", or "Undefined") |
Sequence | Hash pointer. The referenced hash should have the following key/value structure: sequence => String (<= 65535 characters) length => Integer (<= 16777215) lengthUnits => String ("bp", "Kb", or "Mb") | This is the DNA sequence associated with the marker.
ISCNMapLocations | Array pointer to a list of hash pointers. The referenced hashes should have the following key/value structure: chrNumber => String (<= 8 characters) chrArm => String (<= 8 characters) band => String (<= 16 characters) bandingMethod => String (<= 32 characters) | These represent cytogenetic map locations associated with the marker.
Organism | Hash pointer. The referenced hash should have the following key/value structure: genusSpecies => String (<= 255 characters) subspecies => String (<= 120 characters) strain => String (<= 120 characters) |

Example:

    $snp = new Genetics::SNP(name => 'EAEx1.1',
    			 importID => 265,
    			 dateCreated => $today,
    			 Keywords => [ {name => "Test Data",
    					dataType => "Boolean",
    					value => 1},
    					   ],
    			 chromosome => "12",
    			 malePloidy => 2,
    			 femalePloidy => 2,
    			 snpType => "Substitution",
    			 functionClass => "Synonymous",
    			 snpIndex => 2,
    			 isConfirmed => 1,
    			 confirmMethod => "Resequencing",
    			 comment => "Highly expressed in muscle",
    			 Organism => {genusSpecies => "Homo sapiens"},
    			 Alleles => [ {name => "C", type => "Nucleotide"},
    				      {name => "T", type => "Nucleotide"} ],
    			 Sequence => {lengthUnits => "bp",
    				      length => 17,
    				      sequence => "ACGTUMRWSYKVHDBXN"},
    			 ISCNMapLocations => [ {chrNumber => "12",
    						chrArm => "p",
    						band => "12.2",
    						bandingMethod => "Geimsa"},
    					     ],
    			) ;

## Genetics::Object::StudyVariable

Required Attributes
Name | Data type and Format | Description
--- | --- | ---
category | String ("Trait", "StaticAffectionStatus", "StaticLiabilityClass", "DynamicAffectionStatus", "Environment", or "Treatment") | Default value = "Trait" DynamicAffectionStatus variables are not yet implemented.
format | String ("Number", "Code", "Date", "DerivedNumber", or "DerivedCode") | The value of format determines the datatype of the Phenotype values associated with the StudyVariable.
isXLinked | Boolean (1 or 0) | Default value = 0
Optional Attributes
Name | Data type and Format | Description
description | String (<= 255 characters) |
lowerBound | Float (12,5) OR String ("YYYY-MM-DD") | Only appropriate for StudyVariables with a format of Number or Date.
upperBound | Float (12,5) OR String ("YYYY-MM-DD") | Only appropriate for StudyVariables with a format of Number or Date.
Codes | Array pointer to a list of hash pointers. The referenced hashes should have the following key/value structure: code => Integer (<= 99) description => String (<= 255 characters) formula => String (<= 65535 characters) | Only appropriate for StudyVariables with a format of Code or DerivedCode. A formula is required for each Code definition if the format is DerivedCode
AffStatDef | Hash pointer. The referenced hash should have the following key/value structure: name => String (<= 120 characters) diseaseAlleleFreq => Float (7,6) pen11 => Float (7,6) pen12 => Float (7,6) pen22 => Float (7,6) AffStatElements => Array reference to a list of anonymous hashes (see Example). | Only appropriate for StudyVariables with a format of DynamicAffectionStatus. DynamicAffectionStatus variables are not yet implemented; the data in this field is stored in the database, but it is not used for anything. When DynamicAffectionStatus variables are implemented, the AffStatDef will automatically map phenotype values onto a trait locus for genetic linkage analysis.
LCDef | Hash pointer. The referenced hash should have the following key/value structure: name => String (<= 120 characters) LiabilityClasses => Array reference to a list of anonymous hashes (see Example). | Only appropriate for StudyVariables with a category of DynamicAffectionStatus. DynamicAffectionStatus variables are not yet implemented; the data in this field is stored in the database, but it is not used for anything. When DynamicAffectionStatus variables are implemented, the LCDef will automatically map phenotype values into liability classes for genetic linkage analysis.

Example:

    $sv = new Genetics::StudyVariable(name => 'EA Aff Stat',
    				  importID => 445,
    				  dateCreated => $today,
    				  Keywords => [ {name => "Test Data",
    						 dataType => "Boolean",
    						 value => 1},
    					      ],
    				  description => "EA Trait Locus",
    				  category => "AffectionStatus",
    				  format => "Code",
    				  isXLinked => 0,
    				  Codes => [ {code => 0,
    					      description => "Unknown EA Status"},
    					     {code => 1,
    					      description => "EA Unaffected"},
    					      {code => 2,
    					      description => "EA Affected"},
    					   ],
    				  AffStatDef => {name => 'EA',
    						 diseaseAlleleFreq => 0.001,
    						 pen11 => 0.0,
    						 pen12 => 0.0,
    						 pen22 => 1.0,
    						 AffStatElements => [ {code => 0,
    								       type => "Unknown",
    								       formula => "'EA Aff Stat' = 0"},
    								      {code => 1,
    								       type => "Unaffected",
    								       formula => "'EA Aff Stat' = 1"},
    								      {code => 2,
    								       type => "Affected",
    								       formula => "'EA Aff Stat' = 2"},
    								    ],
    						},
    				  LCDef => {name => 'EA Default LC',
    					    LiabilityClasses => [ {code => 0,
    								   description => "Unknown Age",
    								   pen11 => 0.0,
    								   pen12 => 0.0,
    								   pen22 => 1.0,
    								   formula => "'Age' = ''"},
    								  {code => 1,
    								   description => "Age less than 40",
    								   pen11 => 0.0,
    								   pen12 => 0.2,
    								   pen22 => 1.0,
    								   formula => "'Age' < 40"},
    								  {code => 2,
    								   description => "Age less than 50",
    								   pen11 => 0.0,
    								   pen12 => 0.3,
    								   pen22 => 1.0,
    								   formula => "'Age' < 50"},
    								  {code => 3,
    								   description => "Age grater than or equal to 60",
    								   pen11 => 0.0,
    								   pen12 => 0.4,
    								   pen22 => 1.0,
    								   formula => "'Age' >= 60"},
    								    ],
    						},
    				 ) ;

## Genetics::Object::Subject

Required Attributes
Name | Data type and Format | Description
--- | --- | ---
gender | String ("Unknown", "Male", "Female", or "Both") |
Optional Attributes
Name | Data type and Format | Description
dateOfBirth | String ("YYYY-MM-DD") |
dateOfDeath | String ("YYYY-MM-DD") |
isProband | Boolean (1 or 0) | Default value = 0.
Mother | Subject Reference |
Father | Subject Reference |
Kindred | Kindred Reference |
Organism | Hash pointer. The referenced hash should have the following key/value structure: genusSpecies => String (<= 255 characters) subspecies => String (<= 120 characters) strain => String (<= 120 characters) |
Haplotypes | Array pointer to a list of hash pointers. The referenced hashes should have the following key/value structure: Haplotype => Haplotype References phase => String ("Unknown", "Maternal", or "Paternal") | Haplotype assignments.

Example:

    $subject = new Genetics::Subject(name => 'JXPed1-1',
    				 importID => 12,
    				 dateCreated => $today,
    				 NameAliases => [ {name => "jb2002",
    						   contactName => "Gregor Mendel"}
    						],
    				 Contact => {name => "J.P. Morgan, II",
    					     comment => "Referring physican"},
    				 DBXReferences => [ {accessionNumber => "abc123",
    						     databaseName => "Clinical Land",
    						     schemaName => "Master",
    						     comment => "Internal Clinical DB"}
    						  ],
    				 Keywords => [ {name => "Study Population",
    						dataType => "String",
    						description => "Internal study identifier.",
    						value => "Test"},
    					       {name => "Foo",
    						dataType => "String",
    						description => "Crap",
    						value => "Bar"},
    					       {name => "Test Data",
    						dataType => "Boolean",
    						value => 1},
    					     ],
    				 gender => "Male",
    				 dateOfBirth => "1937-08-18",
    				 dateOfDeath => "1997-02-15",
    				 isProband => 1,
    				 Mother => {name => "JXPed1-2", importID => 32},
    				 Father => {name => "JXPed1-3", importID => 22},
    				 Kindred => {name => "JXPed1", importID => 264},
    				 Organism => {genusSpecies => "Homo sapiens"},
    				) ;

## Genetics::Object::TissueSample

Required Attributes
Name | Data type and Format | Description
--- | --- | ---
tissue | String (<= 120 characters) |
Optional Attributes
Name | Data type and Format | Description
dateCollected | String ("YYYY-MM-DD") | Date the sample was collected/prepared.
amount | Float (6,3) |
amountUnits | String ("g", "mg", "ug", or "ng") |
Subject | Subject reference | Reference to the Subject from which the Sample is derived.
Genotypes | Array pointer to a list of Genotype references | Reference(s) to Genotype(s) derived from the Sample.
DNASamples | Array pointer to a list of DNASample references | Reference(s) to DNASample(s) derived from the TissueSample.

Example:

    $sample = new Genetics::TissueSample(name => 'SM20.1',
    				     importID => 273,
    				     dateCreated => $today,
    				     comment => "Hard to make DNA from this",
    				     Keywords => [ {name => "Test Data",
    						    dataType => "Boolean",
    						    value => 1},
    						 ],
    				     tissue => "Muscle",
    				     dateCollected => "2001-01-18",
    				     amount => 150,
    				     amountUnits => "g",
    				     Subject => {name => 'EAPed20.1',
    						 importID => 12},
    				     DNASamples => [ {name => 'SM20.1-3',
    						      importID => 272},
    						   ]
    				    ) ;

# Overview of the GenPerl API

The GenPerl API functionality is separated into the following packages.

- Genetics::API. This package contains general API methods that don't fit anywhere else right now. This is the only package that needs to be imported into your programs.
- The Genetics::API::DB packages contain methods for managing the persistance of GenPerl objects in a relational database.
  - Genetics::API::DB::Insert.
  - Genetics::API::DB::Read.
  - Genetics::API::DB::Update.
  - Genetics::API::DB::Delete.
  - Genetics::API::DB::Query.
- Genetics::API::Analysis. This package contains methods for the analysis of data contained in GenPerl objects.

## Genetics::API

An instance of Genetics::API must be instantiated in order to interact with GenPerl objects in a database (or to access any other API methods, for that matter). If the initialization parameters include a 'DSN' value, the Genetics::API constructor will use these values to construct a data source name string. This data source name, along with the user and password parameters are passed directly to DBI->connect for the creation of a database handle. It is possible to instantiate an API instance without the creation of a database connection (just ommit the DSN attribute from the arguments passed to the constructor). In this case the user and password are igored, however this may be changed in the future.

Example:

    $api = new Genetics::API(DSN => {driver => "mysql",
    			         host => $Host,
    			         database => $Database},
    			 user => $UserName,
    			 password => $Password) ;

## Genetics::API::DB::Insert

When saving new objects, references between object may be made either via importIDs (saved as Keywords) or by absolute object IDs. IDs can only be used to reference objects that have already been saved to the database, and have thus been given a database ID. Otherwise, importIDs must be used.

When a new object identified with an importID is saved to the database, an id is generated for that object. However, it's importID is also saved as a Keyword (w/ keywordTypeID = 1). These keywords are the mechanism the API uses to make connections between objects being imported with importIDs. In theory, an object could always be referenced via the importID with which it was identified when it was first imported. The API supports this. However, unless the importIDs you use are unique amongst all objects ever imported into a given schema, this approach will eventually lead to trouble.

Thus, the best plan is to stick with the following rule. If you want to reference an object that already exists in the database, use ids; if you are making references to objects that are being imported concurrently as part of the same import session, use importIDs.

The various save methods return the database id of the saved object.

Example:

The following code will create a new SNP. It also creates a new Genotype. The new Genotype references the new SNP, but a Subject that has previously been saved to the database.

    $snp = new Genetics::SNP(name => 'SNPxyz',
    			 importID => 121,
    			 dateCreated => $today,
    			 chromosome => "X",
    			 malePloidy => 1,
    			 femalePloidy => 2,
    			 snpType => "Substitution",
    			 functionClass => "Synonymous",
    			 snpIndex => 2,
    			 isConfirmed => 1,
    			 confirmMethod => "Resequencing",
    			 Organism => {genusSpecies => "Homo sapiens"},
    			 Alleles => [ {name => "C", type => "Nucleotide"},
    				      {name => "T", type => "Nucleotide"} ],
    			) ;

    $gt = new Genetics::Genotype(name => '1-SNPxyz',
    			     importID => 122,
    			     dateCreated => $today,
    			     isActive => 1,
    			     dateCollected => $today,
    			     Subject => {name => "EAPed20.1", id => 1},
    			     Marker => {name => "SNPxyz", importID => 121},
    			     AlleleCalls => [ {alleleName => "C",
    					       alleleType => "Nucleotide",
    					       phase => "Unknown"
    					      },
    					      {alleleName => "T",
    					       alleleType => "Nucleotide",
    					       phase => "Unknown"
    					    ]
    			    ) ;

    $id = $api->insertSNP($snp) ;
    $id = $api->insertGenotype($gt) ;

## Genetics::API::DB::Read

Currently, objects can be retrieved from the databse by id and by name.

Example:

This code will retrieve and print the Subject with id = 1.

        $subject = $api->getSubject(1) ;
        $subject->print() ;

## Genetics::API::DB::Update

Objects in the database are identified for update by id only.

The following describes the update behavior implemented by the methods in this package: - The data in each object field will completely replace the data in the database for that field. - Data for fields not present in an object will not be affected. - In order to delete data for a particular field, the value of that field should be set to "DELETE" (irrespective of the normal datatype of thet field). - In order to add to existing data for a particular field, use an appropriate method in Genetics::API or handle it manually.

Examples:

The following code will retrieve the Subject with id = 3, change its name, and then re-save the Subject with its new name back to the databse.

        $subject = $api->getSubject(3) ;
        $subject->field("name", "foo") ;
        $api->updateSubject($subject) ;

The following code will completely replace the set of alleles associated with the SNP with id = 11:

        @alleles = ( {name => "A", type => "Nucleotide"},
    	         {name => "C", type => "Nucleotide"} ) ;
        $snp = $api->getSNP(11) ;
        $snp->field("Alleles", \@alleles) ;
        $api->updateSNP($snp) ;

The following code will add an allele to those associated with the SNP with id = 11:

        $snp = $api->getSNP(11) ;
        $alleleListPtr = $snp->field("Alleles") ;
        push( @$alleleListPtr, {name => "A", type => "Nucleotide"} ) ;
        $snp->field("Alleles", $alleleListPtr) ;
        $api->updateSNP($snp) ;

## Genetics::API::DB::Delete

Currently, objects can be deleted from the databse by id only.

Example:

The following code will delete Genotypes:

    foreach $id ( @badGenotypeIDs ) {
      $rv = $api->deleteGenotype($id) ;
      defined $rv or print "Error deleting Genotype w/ ID $id\n" ;
    }

## Genetics::API::DB::Query

Example:

The following code ...

## Genetics::API::Analysis

Example:

The following code...

# The GenPerl Database

In order to manage the persistance of GenPerl objects, the GenPerl API requires a relational database instance in which to store the data. All database interaction in the API is implemented using DBI, and thus the API could, in theory, support the use of any RDBMS for which a DBI driver module exists. However, right now, the API will only work with MySQL. I chose MySQL mainly because it's free, fast, and relatively simple to administrate. The API takes advantage of a couple of MySQL features - mainly AUTOINCREMENT - which are not present in all RDBMSs. The API code would probably need to be changed in order to use any database. Of course, a suitable schema must be created within the database instance in which the data will be stored and the DDL (see below) would need to be altered such that the column data types are something that the other database will understand.

The DDL in this script can be used to create an appropriate schema in MySQL.

# Using GenPerl

The sections below contain example code with comments explaining what the code is doing.

- Create affected and unaffected Subject Clusters and do a simple chi-square association test
- Graph allele frequencies in affected and unaffected Subjects
- Write linkage format files

### Create affected and unaffected Subject Clusters and do a simple chi-square association test.

    #!/usr/local/bin/perl -w

    # Create an API instance
    use Genetics::API ;
    $api = new Genetics::API(DSN => {driver => "mysql",
    				 host => $Host,
    				 database => $Database},
    			 user => slm,
    			 password => GetMysqlPassword()) ;

# Get the StudyVariable on which the Clusters will be based

    $sv = $api->getObjectByName("Aff") ;

# Get affected Subjects and create a Cluster

    @affSubjects = $api->getSubjectsByPhenotype($sv, 2) ;
    $affCluster = $api->createCluster("HT Affecteds", \@affSubjects) ;

# Get unaffected Subjects and create a Cluster

    @unaffSubjects = $api->getSubjectsByPhenotype($sv, 1) ;
    $unaffCluster = $api->createCluster("Normals", \@unaffSubjects) ;

# Get the Marker whose allele distributions will be tested

    $marker = $api->getObjectByName("agtT174M") ;

# Do the test

    $api->chiSquareAssocTest($marker, "Nucleotide",
    			 $affCluster, $unaffCluster) ;

# This will print something along the lines of:

    Allele counts:

                    C       T
    HT_Affecteds    873 125
    Normals         736 64

    The Chi-Square value is 9.66582997149079, with 1 degrees of freedom

    The probability that the difference in agtT174M allele distributions between
    HT_Affecteds and Normals is due to chance is less than 0.01.

### Graph allele frequencies in affected and unaffected Subjects.

    #!/usr/local/bin/perl -w

    #Create an API instance
    use Genetics::API ;
    $api = new Genetics::API(DSN => {driver => "mysql",
    				 host => $Host,
    				 database => $Database},
    			 user => slm,
    			 password => GetMysqlPassword()) ;

    # Get the Clusters containing the Subjects whose allele frequencies will be graphed
    $affCluster = $api->getObjectByName("HT Affecteds") ;
    $unaffCluster = $api->getObjectByName("Normals") ;

    # Get the Marker whose allele frequencies will be graphed
    $marker = $api->getObjectByName("agtT174M") ;

    # Graph the frequencies
    $api->graphAlleleFreqs(
    		       MARKER => $marker,
    		       FREQSOURCES => [ $affCluster, $unaffCluster ],
    		       ALLELETYPE => "Nucleotide"
    		      ) ;
    # This will display a graph along the lines of:

### Write linkage format files

    #!/usr/local/bin/perl -w

    #Create an API instance
    use Genetics::API ;
    $api = new Genetics::API(DSN => {driver => "mysql",
    				 host => $Host,
    				 database => $Database},
    			 user => slm,
    			 password => GetMysqlPassword()) ;

    # Get a Kindred to be used
    $kindred = $api->getObjectByName("Ped20") ;

    # Get the founder Subjects in the Kindred.  These will be used as the allele frequency source
    push(@founders, $api->getFounders($kindred)) ;

    # Get the Markers to be used
    @markers = $api->getObjectsByType("Marker") ;

    # Get the StudyVariable to be used as the trait locus
    $sv = $api->getObjectByName("Aff") ;

    # Get the StudyVariable the defines liability classes assocoated with the trait locus
    $lc = $api->getObjectByName("Aff LC") ;

    open(DAT, "> ea20.dat") or die "Can't write file: $!" ;
    open(PRE, "> ea20.pre") or die "Can't write file: $!" ;

    # Write the files
    $api->writeLinkageFiles(
    			KINDREDS => [ $kindred ],
    			MARKERS => \@markers,
    			AFS => \@founders,
    			TRAIT => $sv,
    			LC => $lc,
    			DATFILE => \*DAT,
    			PEDFILE => \*PRE,
    		       ) ;

    close DAT ;
    close PRE ;

    # This will produce the following:

Locus file:

    10 0 0 5
    0 0.0 0.0 0
     1 2 3 4 5 6 7 8 9 10
    1 2  << affection status
    0.9999 0.000100  << allele frequencies
    5 << number of liability classes
    0.000000 0.000000 0.000000  << penetrance values
    0.020000 0.020000 1.000000  << penetrance values
    0.040000 0.040000 1.000000  << penetrance values
    0.050000 0.050000 1.000000  << penetrance values
    0.100000 0.100000 1.000000  << penetrance values
    3 6 # D12S91
    0.5 0.1667 0.3333 0.0001 0.0001 0.0001  << allele frequencies
    3 6 # D12S100
    0.0833 0.0833 0.5 0.25 0.0001 0.0833  << allele frequencies
    3 7 # CACNL1A1
    0.0001 0.0714 0.5 0.0714 0.0714 0.2857 0.0001  << allele frequencies
    3 5 # D12S372
    0.0001 0.3571 0.3571 0.2143 0.0714  << allele frequencies
    3 7 # pY2/1
    0.0001 0.25 0.0833 0.0001 0.0001 0.1667 0.5  << allele frequencies
    3 9 # pY21/1
    0.0001 0.0001 0.1429 0.3571 0.0714 0.2143 0.0001 0.1429 0.0714  << allele frequencies
    3 5 # KCNA5
    0.3333 0.1667 0.25 0.25 0.0001  << allele frequencies
    3 8 # D12S99
    0.0714 0.0714 0.0714 0.2857 0.1429 0.2143 0.0714 0.0714  << allele frequencies
    3 7 # D12S93
    0.1 0.1 0.1 0.2 0.2 0.0001 0.3  << allele frequencies
    0 0
     0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1  << map
    1 0.10000 0.45000

Pedigree file:

    Ped20 1 1001 1000 1 2 3  3 1  3 6  9 10  5 3  6 2  3 7  3 2  1 3  6 4
    Ped20 1001 0 0 1 1 4  3 1  3 3  9 9  5 4  6 6  4 3  4 3  5 1  6 6
    Ped20 1000 2002 2001 2 2 4  1 3  4 6  10 10  2 3  1 2  7 7  3 2  6 3  4 7
    Ped20 100 1001 1000 1 1 3  1 1  3 4  9 10  5 2  6 1  4 7  3 3  1 6  6 7
    Ped20 2002 0 0 1 1 4  1 1  1 4  9 10  2 2  1 5  5 7  3 4  4 6  4 7
    Ped20 2001 0 0 2 2 4  0 0  0 0  0 0  0 0  0 0  0 0  0 0  0 0  0 0
    Ped20 1002 2002 2001 1 1 4  1 2  4 5  10 7  2 2  1 1  7 10  3 4  6 5  4 7
    Ped20 1006 2002 2001 2 2 4  1 3  4 6  10 10  2 3  1 2  7 7  3 2  6 3  4 7
    Ped20 1007 0 0 1 1 3  3 3  4 6  1 8  2 3  0 0  4 7  2 4  3 6  3 4
    Ped20 1008 2002 2001 2 2 4  1 3  1 6  9 10  2 3  5 2  5 7  4 2  4 3  4 4
    Ped20 1009 0 0 1 1 4  1 2  3 3  2 9  2 3  1 1  4 5  1 2  2 5  1 2
    Ped20 199 0 0 2 1 3  0 0  0 0  9 10  3 4  2 5  4 9  0 0  4 4  0 0
    Ped20 1010 2002 2001 2 2 4  1 3  1 6  9 10  2 3  5 2  5 7  4 2  4 3  4 4
    Ped20 1011 0 0 1 1 4  0 0  0 0  0 0  0 0  0 0  0 0  0 0  0 0  0 0
    Ped20 102 1001 1000 1 2 2  3 3  3 6  9 10  4 3  6 2  4 7  4 2  5 3  6 4
    Ped20 103 0 0 2 1 2  2 1  3 2  10 10  4 3  6 6  4 3  3 1  6 4  0 0
    Ped20 104 0 0 2 1 3  3 1  4 3  9 9  3 2  6 6  10 5  1 1  8 7  6 3
    Ped20 113 1007 1006 2 2 2  3 3  6 4  1 10  3 2  6 1  4 7  2 3  6 3  4 7
    Ped20 114 1007 1006 2 2 2  3 3  6 6  8 10  3 3  6 2  4 7  2 2  3 3  4 4
    Ped20 115 1009 1008 1 2 2  1 3  3 6  9 10  3 2  1 2  4 7  2 2  5 3  1 4
    Ped20 116 1011 1010 1 2 3  3 3  2 6  9 10  3 3  1 2  4 7  1 2  3 3  4 4
    Ped20 117 1011 1010 2 1 3  1 3  4 1  8 9  2 3  6 5  5 5  4 4  4 4  4 4
    Ped20 9099 100 199 1 1 2  0 0  0 0  9 9  5 3  6 2  4 4  0 0  1 4  0 0
    Ped20 9098 100 199 2 0 2  0 0  0 0  10 10  2 4  1 5  7 9  0 0  6 4  0 0
    Ped20 9097 100 199 1 1 1  0 0  0 0  9 9  5 3  6 2  4 4  0 0  1 4  0 0
    Ped20 9003 102 103 1 2 1  3 2  6 2  10 10  4 3  2 6  7 4  2 3  3 4  4 6
    Ped20 9004 102 103 1 1 1  3 1  6 3  10 10  3 3  6 6  4 3  4 1  5 6  4 6
    Ped20 9005 1 104 2 2 1  3 1  6 4  10 9  3 3  2 6  7 5  2 1  3 7  4 6
    Ped20 9006 1 104 1 0 1  1 1  3 4  9 9  5 3  6 6  3 5  3 1  0 0  0 0