# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: 'Python 3.9.5 64-bit (''base'': conda)'
#     name: python3
# ---

# # PROBLEM 1
#
# * Define a class for a circle, defined using its center and its radius
# * Define the methods to calculate the perimeter and the area (use math.pi for pi)
# * Define a method to check if an object of type circle intersect another object of class circle
# * Define a method to check if self completely includes another object of type circle

# +
import math
from collections import namedtuple

Point = namedtuple('Point', 'x y')

class Circle:
    
    def __init__(self, center: Point, radius):
      self.center = center
      self.radius = radius
    
    def perimeter(self):
        """
        Perimeter Calculate perimeter of the circle
        """
        return 2*math.pi*self.radius
    
    def area(self):
        return math.pi * self.radius**2
    
    def intersect(self, oth):
        """intersect Check if an object of type circle intersect another object of class circle

        Parameters
        ----------
        oth : Circle
            The object to check if it intersect with self
        """
        return math.sqrt((self.center.x-oth.center.x)**2 + (self.center.y-oth.center.y)**2) < self.radius + oth.radius

    def includes(self, oth):
        """includes check if self completely includes another object of type circle

        Parameters
        ----------
        oth : Circle
            The object to check if it is included in self
        """
        return self.radius > math.sqrt((self.center.x-oth.center.x)**2 + (self.center.y-oth.center.y)**2) + oth.radius


# +
cerchio = Circle(Point(0, 0),5)
cerchio2 = Circle(Point(1, 0),1)
print(cerchio.includes(cerchio2))

cerchio3 = Circle(Point(4, 0),3)
print(cerchio.intersect(cerchio3))
# -

# # PROBLEM 2
# Define a class to deal with day time values in the 12h format (e.g. 9:50 a.m). The class should be initialized providing a string with the format HH:MM am/p.m; and it should include the following methods:
# * relational operators, for testing earlier/later times during the day
# * +/- operators (do not care if the sum give a time value in another day)

from datetime import datetime,timedelta
class TimeValues:
    """ Define a class to deal with day time values in the 12h format (e.g. 9:50 a.m). The class should be initialized providing a string with the format HH:MM am/p.m; and it should include the following methods:
        * relational operators, for testing earlier/later times during the day
        * +/- operators (do not care if the sum give a time value in another day)
    """ 
    
    def __init__(self, time: str):
        self._time =  datetime.strptime(time.replace(".",""), "%H:%M %p")
        self._time = self._time.replace(year=2020)
        print(self._time)
    
    def getTime(self) -> datetime:
        return self._time
    
    def __lt__(self, other):
        if not(isinstance(other, TimeValues)): raise TypeError("The other must be a TimeValues instance")
        return self._time < other._time
    
    def __gt__(self, other):
        if not(isinstance(other, TimeValues)): raise TypeError("The other must be a TimeValues instance")
        return self._time > other._time
    
    def __eq__(self, other):
        if not(isinstance(other, TimeValues)): raise TypeError("The other must be a TimeValues instance")
        return self._time == other._time
    
    def __add__(self,other):
        if not(isinstance(other, TimeValues)): raise TypeError("The other must be a TimeValues instance")
        new_time = self._time + timedelta(hours=other.getTime().hour, minutes=other.getTime().minute)
        return TimeValues(str(new_time.strftime("%H:%M %p")))
    
    def __sub__(self,other):
        if not(isinstance(other, TimeValues)): raise TypeError("The other must be a TimeValues instance")
        new_time = self._time - timedelta(hours=other.getTime().hour, minutes=other.getTime().minute)
        return TimeValues(str(new_time.strftime("%H:%M %p")))
    
    def __str__(self):
        return self._time.strftime("%H:%M %p")


test_day = TimeValues("2:00 p.m")
test_day2 = TimeValues("1:50 p.m")
print(test_day > test_day2)
print(test_day < test_day2)
print(test_day == test_day2)
print(test_day + test_day)

# * Sort the list of random times included in list_times

n= 20
import numpy as np
list_times = ['{0:02d}:{1:02d} {2:s}'.format(h,m,sfx) 
              for h,m,sfx in zip(np.random.randint(low = 0, high = 12, size = n),
                                 np.random.randint(low = 0, high = 59, size = n),
                                 ['am' if x > 0.5 else 'pm' for x in np.random.randint(low = 0, high = 2, size = n)]) ]



# # PROBLEM 3
# Define a class to include all the information extracted from a sequencing experiment. The result of the sequencing experiment is provided in the form of a text file with the following format:
#
# *chromosome,starting_position,ending_position,reference_letter,alternative_allele*
#
# As an example: *chr17,13905,13905,G,A*
#
# Define the len method
#
# Implement the getitem method, so that:
# * data[i], return the i-th variant (i is an integer)
# * data['chr1'], return a list of all the variants in chromosome 'chr1'
# * data['chr1:13400-14000'], return a list of all the variants in chromosome 'chr1' that starts at position 13400 and ends before position 14000 (not included)
#
# Suggestion: Define a class for the variants and a class for the experiment that includes a sequence of variants. Test the classes using the data in the file *ex5_1.txt*
#
# Implement the contain method, so that:
# * if an object of type Variant is provided, it checks if that variant is present in the experiment
# * if a genomic position in the form of a tuple (chromosome, starting_position, ending_position) is provided, it checks if that position is mutated in the experiment (there is a variant in that range)
#

class Variant:
    
    def __init__(self, chromosome: str, starting_position: int, ending_position: int, reference_letter: chr, alternative_allele: chr):
        """__init__ Class representing a variant

        Parameters
        ----------
        chromosome : str
            [description]
        starting_position : int
            [description]
        ending_position : int
            [description]
        reference_letter : chr
            [description]
        alternative_allele : chr
            [description]
        """
        self._chromosome : str = chromosome
        self._starting_position : int = starting_position
        self._ending_position : int = ending_position
        self._reference_letter : chr = reference_letter
        self._alternative_allele : chr = alternative_allele
    
    @property
    def chromosome(self):
        return self._chromosome
    
    @property
    def starting_position(self):
        return self._starting_position
    
    @property
    def ending_position(self):
        return self._ending_position
    
    @property
    def reference_letter(self):
        return self._reference_letter
    
    @property
    def alternative_allele(self):
        return self._alternative_allele
    
    def __repr__(self):
        return '{0:s}:{1:d}-{2:d} {3:s}->{4:s}'.format(self.chromosome, self.starting_position, self.ending_position, self.reference_letter, self.alternative_allele)


# +
from typing import List
class Experiment: 
    
    def __init__(self, variants: List[Variant]):
        """__init__ [summary]

        Parameters
        ----------
        variants : List[Variant]
            [description]
        """
    
        self._variants = variants
    
    @property
    def variants(self): 
        return self._variants
    
    def __setitem__(self, index, data):
          self._variants[index] = data
      
    def __getitem__(self,index):
        """__getitem__ Return the selected variant, it has 3 different behavior wrt of the index passed

        Parameters
        ----------
        index : object
            * (int) -> 1
            * (str) -> 'chr1'
            * (str) -> 'chr1:13400-14000'

        Returns
        -------
            * (int) : return the (int)-th variant
            * (str) -> 'chr1': return a list of all the variants in chromosome 'chr1'
            * (str) -> 'chr1:13400-14000', return a list of all the variants in chromosome 'chr1' that starts at position 13400 and ends before position 14000 (not included)
        """
        if isinstance(index, int): return self.variants[index]
        if isinstance(index, str): 
            # Case 2-3
            if len(index.split(':')) > 1: 
                # Case 3
                chromosome,positioning = index.split(':')
                starting_position,ending_position = positioning.split("-")
                return list(filter(lambda variant: variant.chromosome == index and variant.starting_position >= starting_position and 
                                        variant.ending_position < ending_position, self.variants))
            # Case 2
            return list(filter(lambda variant: variant.chromosome == index, self.variants))
        raise IndexError("Follow the documentation")
    
    def __contains__(self, oth):
        """__contains__ Contains method for class Experiment, it has different behavior depending on the type of oth

        Parameters
        ----------
        oth : object/varying
            (Variant): check if that variant is present in the experiment
            (tuple(chromosome, starting_position, ending_position)): check if that position is mutated in the experiment (there is a variant in that range)
        """
        if isinstance(oth, Variant): return oth in self._variants
        if isinstance(oth, tuple): 
            if len(self.__getitem__(f"{oth[0]}:{oth[1]}-{oth[2]}")) > 1: return True
        return False
            
      
# -

# Find the number of variants in ex5_1.txt and ex5_2.txt

# +
variant_collection_1 : List[Variant] = []
with open("ex5_1.txt", "rt") as ex5_1:
    for line in ex5_1:
        line = line.split(",")
        variant_collection_1.append(Variant(
            chromosome=line[0].strip(),
            starting_position=int(line[1]),
            ending_position=int(line[2]),
            reference_letter=line[3].strip(),
            alternative_allele=line[4].strip()
        ))
print(f"Number of variants in experiment_1: {len(variant_collection_1)}")    
    
variant_collection_2 : List[Variant] = []
with open("ex5_2.txt", "rt") as ex5_2:
    for line in ex5_2:
        line = line.split(",")
        variant_collection_2.append(Variant(
            chromosome=line[0].strip(),
            starting_position=int(line[1]),
            ending_position=int(line[2]),
            reference_letter=line[3].strip(),
            alternative_allele=line[4].strip()
        ))
print(f"Number of variants in experiment_2: {len(variant_collection_2)}")    
  
experiment_1 = Experiment(variant_collection_1)
experiment_2 = Experiment(variant_collection_2)
# -

# Find the number of variants in common between ex5_1.txt and ex5_2.txt

print('Variants in common: ', len({str(variant) for variant in experiment_1}.intersection({str(variant) for variant in experiment_1})))

# Check if the chr17 is mutated at position 72584764 in exp1

print(("chr17",72584764-100,72584764+100) in experiment_1)

StarcoderdataJupyterScriptsDedupFiltered