[BioRuby] Map objects => comparable

jan aerts (RI) jan.aerts at bbsrc.ac.uk
Wed Feb 8 16:46:56 UTC 2006


Hi all,

I've just looked into creating a simple Map object (could hold data for
linkage maps, RH maps and the like). However, being rather new to ruby,
I bump into what probably turns out to be a simple problem.

I originally thought of creating a SimpleMap and Marker class, each
containing a @mappings array. However, this gave problems when
implementing a sensible <=> method in the Marker class (to be able to
use Comparable mixin). The reason is that a single marker can be mapped
to different maps, and I couldn't see a way to traverse only those
mappings for a marker that would go to a certain map.

Therefore, I included a Mapping class, which would provide the link
between maps and markers and in which a sane <=> method could be
written.

Result: it looks like these three classes work nicely together (see
test-code below: I can sort markers on position within a given map, or
print the last marker on the map). However, I think that this setting
does not make it possible to do a between? or '>' between mappings...

Could I ask anyone to take a look at the code below and tell me how to
use/alter it to be able to:
* Test if a marker is located upstream of another marker (e.g. based on
the '<'-method from the Comparable mixin)
* Test is a marker is between two other markers (e.g. based on the
'between?'-method from the Comparable mixin; in the code example below:
test if my_marker3 lies between my_marker1 and my_marker2 on my_map1)

Explanation of setup of maps/markers:
# One marker can be mapped to different maps. (<= this is what's giving
the problem)
# For each marker that is mapped to a map, or map that gets a marker
added, basically two things happen: a mapping is added to the Marker
object as well as to the SimpleMap object

Many thanks,
jan.

<BEGIN OF MODULE>
module Bio
  module Map
    # This class handles the essential storage of name, species, type
and units of a map.
    class SimpleMap
      include Enumerable
      # Builds a new Bio::Map::SimpleMap object
      def initialize (name = nil, species = nil, units = nil, type =
nil)
        @name, @species, @units, @type = name, species, units, type
        @mappings = Array.new()
      end
      attr_accessor :name, :species, :units, :type, :mappings
      
      # Add a MappedMarker object to the map
      def add_mapping(marker, position)
        unless marker.kind_of?(Bio::Map::Marker)
          raise "[Error] marker is not a Bio::Map::Marker object"
        end
        my_mapping = Bio::Map::Mapping.new(self, marker, position)
        @mappings.push(my_mapping)
        unless marker.mapped_to?(self)
          marker.mappings.push(my_mapping)
        end
      end
      
      # Check whether a Bio::Map::Marker is mapped to this
Bio::Map::SimpleMap.
      def contains?(marker)
        contains = false
        @mappings.each do |mapping|
          if mapping.marker == marker
            contains = true
            return contains
          end
        end
        return contains
      end
      
      # Go through all Bio::Map::Mapping objects linked to this
Bio::Map::SimpleMap.
      def each
        @mappings.each do |mapping|
          yield mapping
        end
      end
      
    end # SimpleMap

    # This class represents the link between SimpleMap and Marker
    class Mapping
      include Comparable
      def initialize (map, marker, position)
        @map, @marker, @position = map, marker, position
      end
      attr_accessor :map, :marker, :position

      # To get comparable working...
      def <=>(other)
        unless self.position.kind_of?(Comparable)
          raise "[Error] markers are not comparable"
        end
        if self.position > other.position
          return 1
        elsif self.position < other.position
          return -1
        elsif self.position == other.position
          return 0
        end
      end

    end # Mapping

    # This class handles markers that are anchored to
Bio::Map::SimpleMap.
    class Marker
      include Enumerable
      def initialize(name)
        @name = name
        @mappings = Array.new()
      end
      attr_accessor :name, :mappings
      
      # Add a new map and position for this marker
      def add_mapping (map, position)
        unless map.kind_of?(Bio::Map::SimpleMap)
          raise "[Error] map is not a Bio::Map::SimpleMap object"
        end
        my_mapping = Bio::Map::Mapping.new(map, self, position)
        @mappings.push(my_mapping)
        unless map.contains?(self)
          map.mappings.push(my_mapping)
        end
      end
      
      # Check whether this Bio::Map::Marker is mapped to a given
Bio::Map::SimpleMap
      def mapped_to?(map)
        mapped = false
        @mappings.each do |mapping|
          if mapping.map == map
            mapped = true
            return mapped
          end
        end
        return mapped
      end
      
      # Go through all Mapping objects linked to this map.
      def each
        @mappings.each do |mapping|
          yield mapping
        end
      end
    end # MappedMarker

  end # Map
end # Bio

if __FILE__ == $0
  my_marker1 = Bio::Map::Marker.new('marker1')
  my_marker2 = Bio::Map::Marker.new('marker2')
  my_marker3 = Bio::Map::Marker.new('marker3')

  my_map1 = Bio::Map::SimpleMap.new('RH_map_ABC (2006)', 'BTA', 'cR',
'RH')
  my_map2 = Bio::Map::SimpleMap.new('consensus', 'GGA', 'cM', 'linkage')

  my_map1.add_mapping(my_marker1, 17)
  my_map1.add_mapping(my_marker2, 5)
  my_marker3.add_mapping(my_map1, 9)

  my_marker3.add_mapping(my_map2, 53)

  my_map1.sort.each do |mapping|
    puts mapping.map.name + "\t" + mapping.marker.name + "\t" +
mapping.position.to_s
  end
  puts my_map1.min.marker.name
  my_map2.each do |mapping|
    puts mapping.map.name + "\t" + mapping.marker.name + "\t" +
mapping.position.to_s
  end
#  p my_map1.between?(my_mappable2,my_mappable3)
#  p my_map1.between?(my_mappable,my_mappable2)
end
<END OF MODULE>

---------The obligatory disclaimer--------
The information contained in this e-mail (including any attachments) is
confidential and is intended for the use of the addressee only.   The
opinions expressed within this e-mail (including any attachments) are
the opinions of the sender and do not necessarily constitute those of
Roslin Institute (Edinburgh) ("the Institute") unless specifically
stated by a sender who is duly authorised to do so on behalf of the
Institute. 




More information about the BioRuby mailing list