Class: Dphil::Character

Inherits:
Object
  • Object
show all
Includes:
LDOutput
Defined in:
lib/dphil/character.rb

Overview

Phylogenetic character for storing states and symbols.

Immutable.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from LDOutput

#as_jsonld, #to_jsonld

Constructor Details

#initialize(id = nil, states = nil) ⇒ Character #initialize(**opts = {}) ⇒ Character

Instantiates a new Character

Overloads:

  • #initialize(id = nil, states = nil) ⇒ Character

    Parameters:

    • id (Integer) (defaults to: nil)

      a character ID

    • states (Hash<Integer, String]) (defaults to: nil)

      taxa and text-states { taxon_id => text_state }

  • #initialize(**opts = {}) ⇒ Character

    Parameters:

    • opts (Hash)

      options or keyword values

    Options Hash (**opts):

    • :id (Integer)

      a character ID

    • :states (Hash<Integer, String])

      taxa and text-states { taxon_id => text_state }



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/dphil/character.rb', line 20

def initialize(id = nil, states = nil, **opts)
  @id = (opts[:id] || id)&.to_s.to_i
  @taxa_states = (opts[:states] || states)
                 .to_h.each_with_object({}) do |(taxon, state), acc|
    next if state.blank?
    taxon = taxon.to_s if taxon.is_a?(Symbol)
    acc[taxon.to_i] = normalize_text(state)
  end

  unique_states = weighted_uniq(@taxa_states.values)
  if unique_states.size > SYMBOL_ARRAY.size
    raise ArgumentError,
          "Too many states (found #{unique_states.size}, " \
          "max #{SYMBOL_ARRAY.size})"
  end

  @states = {}
  @state_totals = unique_states
  unique_states.each_key.with_index do |state, index|
    @states[SYMBOL_ARRAY[index]] = state
  end
  instance_variables.each { |ivar| instance_variable_get(ivar).freeze }
end

Instance Attribute Details

#idInteger (readonly)

Returns character ID.

Returns:

  • (Integer)

    character ID



46
47
48
# File 'lib/dphil/character.rb', line 46

def id
  @id
end

#state_listArray<String> (readonly)

Returns text-states.

Returns:

  • (Array<String>)

    text-states



66
67
68
# File 'lib/dphil/character.rb', line 66

def state_list
  @state_list ||= states.values.freeze
end

#state_totalsHash<String, Integer> (readonly)

Returns character state totals by text-state.

Returns:

  • (Hash<String, Integer>)

    character state totals by text-state



78
79
80
# File 'lib/dphil/character.rb', line 78

def state_totals
  @state_totals
end

#statesHash<String, String> (readonly)

Returns text-states by symbol.

Returns:

  • (Hash<String, String>)

    text-states by symbol



56
57
58
# File 'lib/dphil/character.rb', line 56

def states
  @states
end

#states_taxaHash<String, Integer> (readonly)

Returns taxa IDs by text-state.

Returns:

  • (Hash<String, Integer>)

    taxa IDs by text-state



98
99
100
101
102
# File 'lib/dphil/character.rb', line 98

def states_taxa
  @states_taxa ||= (states.each_value.each_with_object({}) do |state, acc|
    acc[state] = taxa_states.select { |_, tstate| state == tstate }.keys
  end).freeze
end

#symbol_listArray<String> (readonly)

Returns symbols.

Returns:

  • (Array<String>)

    symbols



72
73
74
# File 'lib/dphil/character.rb', line 72

def symbol_list
  @symbol_list ||= states.keys.freeze
end

#symbol_totalsHash<String, Integer> (readonly)

Returns character state totals by symbol.

Returns:

  • (Hash<String, Integer>)

    character state totals by symbol



82
83
84
# File 'lib/dphil/character.rb', line 82

def symbol_totals
  @symbol_totals ||= state_totals.transform_keys { |state| symbols[state] }.freeze
end

#symbolsHash<String, String> (readonly)

Returns symbols by text-state.

Returns:

  • (Hash<String, String>)

    symbols by text-state



60
61
62
# File 'lib/dphil/character.rb', line 60

def symbols
  @symbols ||= states.invert.freeze
end

#symbols_taxaHash<String, Integer> (readonly)

Returns taxa IDs by symbol.

Returns:

  • (Hash<String, Integer>)

    taxa IDs by symbol



106
107
108
# File 'lib/dphil/character.rb', line 106

def symbols_taxa
  @symbols_taxa ||= states_taxa.transform_keys { |state| symbols[state] }.freeze
end

#taxaSet<Integer> (readonly)

Returns taxon IDs.

Returns:

  • (Set<Integer>)

    taxon IDs



50
51
52
# File 'lib/dphil/character.rb', line 50

def taxa
  @taxa ||= Set.new(taxa_states.keys).freeze
end

#taxa_statesHash<Integer, String> (readonly)

Returns text-states by taxon ID.

Returns:

  • (Hash<Integer, String>)

    text-states by taxon ID



88
89
90
# File 'lib/dphil/character.rb', line 88

def taxa_states
  @taxa_states
end

#taxa_symbolsHash<Integer, String> (readonly)

Returns symbols by taxon ID.

Returns:

  • (Hash<Integer, String>)

    symbols by taxon ID



92
93
94
# File 'lib/dphil/character.rb', line 92

def taxa_symbols
  @taxa_symbols ||= taxa_states.transform_values { |state| symbols[state] }.freeze
end

Instance Method Details

#as_json(options = nil) ⇒ Object



178
179
180
# File 'lib/dphil/character.rb', line 178

def as_json(options = nil)
  to_h.as_json(options)
end

#constant?Boolean

Check if the character is invariant

Returns:

  • (Boolean)

    whether the character is constant (invariant)



161
162
163
# File 'lib/dphil/character.rb', line 161

def constant?
  @constant ||= states.size <= 1
end

#get_state(symbol) ⇒ String?

Get state from symbol

Parameters:

  • symbol (String)

    a symbol

Returns:

  • (String, nil)

    the associated text-state, or Nil if not found



113
114
115
# File 'lib/dphil/character.rb', line 113

def get_state(symbol)
  states[normalize_text(symbol)]
end

#get_state_taxon(taxon_id) ⇒ String?

Get state from taxon

Parameters:

  • taxon_id (Integer)

    a taxon ID

Returns:

  • (String, nil)

    the associated text-state, or Nil if not found



141
142
143
# File 'lib/dphil/character.rb', line 141

def get_state_taxon(taxon_id)
  taxa_states[taxon_id.to_i]
end

#get_symbol(state) ⇒ String?

Get symbol from state

Parameters:

  • state (String)

    a text-state

Returns:

  • (String, nil)

    the associated symbol, or Nil if not found



120
121
122
# File 'lib/dphil/character.rb', line 120

def get_symbol(state)
  symbols[normalize_text(state)]
end

#get_symbol_taxon(taxon_id) ⇒ String?

Get symbol from taxon

Parameters:

  • taxon_id (Integer)

    a taxon ID

Returns:

  • (String, nil)

    the associated symbol, or Nil if not found



148
149
150
# File 'lib/dphil/character.rb', line 148

def get_symbol_taxon(taxon_id)
  taxa_symbols[taxon_id.to_i]
end

#get_taxa_state(state) ⇒ Array<Integer>

Get taxa from state

Parameters:

  • symbol (String)

    a text-state

Returns:

  • (Array<Integer>)

    the associated taxa IDs



127
128
129
# File 'lib/dphil/character.rb', line 127

def get_taxa_state(state)
  states_taxa[normalize_text(state)]
end

#get_taxa_symbol(symbol) ⇒ Array<Integer>

Get taxa from symbol

Parameters:

  • symbol (String)

    a symbol

Returns:

  • (Array<Integer>)

    the associated taxa IDs



134
135
136
# File 'lib/dphil/character.rb', line 134

def get_taxa_symbol(symbol)
  symbols_taxa[normalize_text(symbol)]
end

#informative?Boolean

Check if character is parsimony-informative (At least 2 variants occurring in at least 2 places)

Returns:

  • (Boolean)

    whether the character provides useful information



155
156
157
# File 'lib/dphil/character.rb', line 155

def informative?
  @informative ||= (states.size > 1 && states_taxa.count { |_, v| v.size > 1 } > 1)
end

#inspectString Also known as: to_s

Returns a string representation of the object.

Returns:

  • (String)

    a string representation of the object.



200
201
202
# File 'lib/dphil/character.rb', line 200

def inspect
  pretty_inspect.chomp
end

#pretty_print(q) ⇒ Object

Pretty-print the object (used by Pry in particular)



184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/dphil/character.rb', line 184

def pretty_print(q)
  q.object_group(self) do
    q.breakable
    q.group(1) do
      q.text "@id=#{id}"
      q.breakable
      q.group(1, "{", "}") do
        q.seplist(states) do |symbol, state|
          q.text "#{state.inspect}(#{symbol})=#{states_taxa[state]}"
        end
      end
    end
  end
end

#to_hObject



165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/dphil/character.rb', line 165

def to_h
  {
    id: id,
    states: states,
    symbols: symbols,
    state_totals: state_totals,
    taxa_states: taxa_states,
    states_taxa: states_taxa,
    is_informative: informative?,
    is_constant: constant?,
  }
end