File Coverage

lib/Pheno/Ranker.pm
Criterion Covered Total %
statement 207 216 95.8
branch 49 74 66.2
condition 26 36 72.2
subroutine 30 30 100.0
pod 0 3 0.0
total 312 359 86.9


line stmt bran cond sub pod time code
1             package Pheno::Ranker;
2              
3 5     5   544030 use strict;
  5         6  
  5         135  
4 5     5   15 use warnings;
  5         10  
  5         203  
5 5     5   1188 use autodie;
  5         43021  
  5         22  
6 5     5   20161 use feature qw(say);
  5         7  
  5         593  
7 5     5   2233 use Data::Dumper;
  5         26597  
  5         314  
8 5     5   28 use File::Basename qw(dirname);
  5         5  
  5         269  
9 5     5   18 use Cwd qw(abs_path);
  5         5  
  5         186  
10 5     5   1080 use File::Spec::Functions qw(catdir catfile);
  5         2011  
  5         259  
11 5     5   2306 use Term::ANSIColor qw(:constants);
  5         28621  
  5         3562  
12 5     5   2724 use Moo;
  5         33558  
  5         15  
13 5     5   10880 use Types::Standard qw(Str Int Num Enum ArrayRef HashRef Undef Bool);
  5         536337  
  5         48  
14 5     5   16681 use File::ShareDir::ProjectDistDir qw(dist_dir);
  5         113607  
  5         38  
15 5     5   1648 use List::Util qw(all);
  5         7  
  5         384  
16 5     5   2517 use Hash::Util qw(lock_hash);
  5         15588  
  5         26  
17 5     5   2527 use Pheno::Ranker::IO;
  5         15  
  5         445  
18 5     5   2743 use Pheno::Ranker::Compare;
  5         16  
  5         397  
19 5     5   28 use Pheno::Ranker::Stats;
  5         5  
  5         234  
20 5     5   2094 use Pheno::Ranker::Graph;
  5         10  
  5         277  
21              
22 5     5   27 use Exporter 'import';
  5         5  
  5         666  
23             our @EXPORT_OK = qw($VERSION write_json);
24              
25             # Personalize warn and die functions
26             $SIG{__WARN__} = sub { warn BOLD YELLOW "Warn: ", @_ };
27             $SIG{__DIE__} = sub { die BOLD RED "Error: ", @_ };
28              
29             # Global variables:
30             $Data::Dumper::Sortkeys = 1;
31             our $VERSION = '1.02';
32             our $share_dir = dist_dir('Pheno-Ranker');
33              
34             # Set development mode
35 5     5   22 use constant DEVEL_MODE => 0;
  5         5  
  5         14723  
36              
37             # Misc variables
38             my ( $config_sort_by, $config_similarity_metric_cohort,
39             $config_max_out, $config_max_number_vars, $config_max_matrix_items_in_ram, @config_allowed_terms );
40             my $default_config_file = catfile( $share_dir, 'conf', 'config.yaml' );
41              
42             ############################################
43             # Start declaring attributes for the class #
44             ############################################
45              
46             has 'config_file' => (
47             is => 'ro',
48             isa =>
49             sub { die "Config file '$_[0]' is not a valid file" unless -e $_[0] },
50             default => $default_config_file,
51             coerce => sub { $_[0] // $default_config_file },
52             trigger => sub {
53             my ( $self, $config_file ) = @_;
54             my $config = read_yaml($config_file);
55              
56             # Set basic configuration parameters
57             $self->_set_basic_config($config);
58              
59             # Validate and set exclusive configuration parameters
60             $self->_validate_and_set_exclusive_config( $config, $config_file );
61              
62             # Set additional configuration parameters on $self
63             $self->_set_additional_config( $config, $config_file );
64              
65             # Lock config data (keys+values)
66             lock_hash(%$config);
67             }
68             );
69              
70             # Private Method: _set_basic_config
71             sub _set_basic_config {
72 19     19   45 my ( $self, $config ) = @_;
73 19   100     83 $config_sort_by = $config->{sort_by} // 'hamming';
74             $config_similarity_metric_cohort = $config->{similarity_metric_cohort}
75 19   100     58 // 'hamming';
76 19   100     59 $config_max_out = $config->{max_out} // 50;
77 19   100     81 $config_max_number_vars = $config->{max_number_vars} // 10_000;
78 19   100     59 $config_max_matrix_items_in_ram = $config->{max_matrix_items_in_ram} // 5_000;
79             }
80              
81             # Private Method: _validate_and_set_exclusive_config
82             sub _validate_and_set_exclusive_config {
83 19     19   47 my ( $self, $config, $config_file ) = @_;
84 19 50 33     191 unless ( exists $config->{allowed_terms}
      33        
85             && ArrayRef->check( $config->{allowed_terms} )
86 19         692 && @{ $config->{allowed_terms} } )
87             {
88 0         0 die "No provided or not an array ref at $config_file\n";
89             }
90 19         27 @config_allowed_terms = @{ $config->{allowed_terms} };
  19         167  
91             }
92              
93             # Private Method: _set_additional_config
94             sub _set_additional_config {
95 19     19   34 my ( $self, $config, $config_file ) = @_;
96              
97             # Setters
98 19   50     72 $self->{primary_key} = $config->{primary_key} // 'id';
99             $self->{exclude_variables_regex} = $config->{exclude_variables_regex}
100 19   100     61 // undef;
101             $self->{exclude_variables_regex_qr} =
102             defined $self->{exclude_variables_regex}
103 19 100       851 ? qr/$self->{exclude_variables_regex}/
104             : undef;
105 19   100     60 $self->{array_terms} = $config->{array_terms} // ['foo'];
106 19   100     48 $self->{array_regex} = $config->{array_regex} // '^([^:]+):(\d+)';
107 19         378 $self->{array_regex_qr} = qr/$self->{array_regex}/;
108             $self->{array_terms_regex_str} =
109 19         30 '^(' . join( '|', map { "\Q$_\E" } @{ $self->{array_terms} } ) . '):';
  169         244  
  19         45  
110 19         352 $self->{array_terms_regex_qr} = qr/$self->{array_terms_regex_str}/;
111 19         52 $self->{format} = $config->{format};
112             $self->{seed} =
113             ( defined $config->{seed} && Int->check( $config->{seed} ) )
114             ? $config->{seed}
115 19 100 66     151 : 123456789;
116              
117 19 100       200 if ( $self->{array_terms}[0] ne 'foo' ) {
118 18 50 33     117 unless ( exists $config->{id_correspondence}
119             && HashRef->check( $config->{id_correspondence} ) )
120             {
121 0         0 die
122             "No provided or not a hash ref at $config_file\n";
123             }
124 18         285 $self->{id_correspondence} = $config->{id_correspondence};
125 18 100 66     67 if ( exists $config->{format} && Str->check( $config->{format} ) ) {
126             die
127             "<$config->{format}> does not match any key from \n"
128 3 50       29 unless exists $config->{id_correspondence}{ $config->{format} };
129             }
130             }
131             }
132              
133             has sort_by => (
134             default => $config_sort_by,
135             is => 'ro',
136             coerce => sub { $_[0] // $config_sort_by },
137             lazy => 1,
138             isa => Enum [qw(hamming jaccard)]
139             );
140              
141             has similarity_metric_cohort => (
142             default => $config_similarity_metric_cohort,
143             is => 'ro',
144             coerce => sub { $_[0] // $config_similarity_metric_cohort },
145             lazy => 1,
146             isa => Enum [qw(hamming jaccard)]
147             );
148              
149             has max_out => (
150             default => $config_max_out,
151             is => 'ro',
152             coerce => sub { $_[0] // $config_max_out },
153             lazy => 1,
154             isa => Int
155             );
156              
157             has max_number_vars => (
158             default => $config_max_number_vars,
159             is => 'ro',
160             coerce => sub { $_[0] // $config_max_number_vars },
161             lazy => 1,
162             isa => Int
163             );
164              
165             has max_matrix_items_in_ram => (
166             default => $config_max_matrix_items_in_ram,
167             is => 'ro',
168             coerce => sub { $_[0] // $config_max_matrix_items_in_ram },
169             lazy => 1,
170             isa => Int
171             );
172              
173             has hpo_file => (
174             default => catfile( $share_dir, 'db', 'hp.json' ),
175             coerce => sub { $_[0] // catfile( $share_dir, 'db', 'hp.json' ) },
176             is => 'ro',
177             isa => sub { die "Error <$_[0]> is not a valid file" unless -e $_[0] },
178             );
179              
180             has poi_out_dir => (
181             default => catdir('./'),
182             coerce => sub { $_[0] // catdir('./') },
183             is => 'ro',
184             isa => sub { die "<$_[0]> dir does not exist" unless -d $_[0] },
185             );
186              
187             has [qw/include_terms exclude_terms/] => (
188             is => 'ro',
189             lazy => 1,
190             isa => sub {
191             my $value = shift;
192             die "<--include_terms> and <--exclude_terms> must be an array ref\n"
193             unless ref $value eq 'ARRAY';
194             foreach my $term (@$value) {
195             die
196             "Invalid term '$term' in <--include_terms> or <--exclude_terms>. Allowed values are: "
197             . join( ', ', @config_allowed_terms ) . "\n"
198             unless grep { $_ eq $term } @config_allowed_terms;
199             }
200             },
201             default => sub { [] },
202             );
203              
204             has cli => (
205             is => 'ro',
206             isa => Bool,
207             default => 0,
208             coerce => sub { $_[0] // 0 },
209             );
210              
211             # Miscellaneous attributes
212             has [
213             qw/target_file weights_file out_file include_hpo_ascendants
214             retain_excluded_phenotypicFeatures align align_basename export export_basename
215             log verbose age cytoscape_json graph_stats/
216             ] => ( is => 'ro' );
217              
218             has [qw/append_prefixes reference_files patients_of_interest/] =>
219             ( default => sub { [] }, is => 'ro' );
220              
221             has [qw/glob_hash_file ref_hash_file ref_binary_hash_file/] => ( is => 'ro' );
222              
223             ##########################################
224             # End declaring attributes for the class #
225             ##########################################
226              
227             sub BUILD {
228              
229             # BUILD: is an instance method that is called after the object has been constructed but before it is returned to the caller.
230             # BUILDARGS is a class method that is responsible for processing the arguments passed to the constructor (new) and returning a hash reference of attributes that will be used to initialize the object.
231              
232 19     19 0 421 my $self = shift;
233              
234             # Miscellaneous checks
235 19 100       41 if ( @{ $self->{append_prefixes} } ) {
  19         55  
236             die "<--append_prefixes> requires at least 2 cohort files!\n"
237 1 50       3 unless @{ $self->{reference_files} } > 1;
  1         4  
238             die "The number of items in <--r> and <--append-prefixes> must match!\n"
239 1 50       2 unless @{ $self->{reference_files} } == @{ $self->{append_prefixes} };
  1         2  
  1         4  
240             }
241 19 50       44 if ( @{ $self->{patients_of_interest} } ) {
  19         257  
242             die "<--patients-of-interest> must be used with <--r>\n"
243 0 0       0 unless @{ $self->{reference_files} };
  0         0  
244             }
245             }
246              
247             # ============================================================
248             # run method
249             # ============================================================
250             sub run {
251 19     19 0 260 my $self = shift;
252              
253             # -----------------------------------------------------
254             # Retrieve configuration parameters from the object
255             # -----------------------------------------------------
256 19         28 my $reference_files = $self->{reference_files};
257 19         30 my $target_file = $self->{target_file};
258 19         27 my $weights_file = $self->{weights_file};
259 19         30 my $export = $self->{export};
260 19         25 my $export_basename = $self->{export_basename};
261 19         24 my $include_hpo_ascendants = $self->{include_hpo_ascendants};
262 19         26 my $hpo_file = $self->{hpo_file};
263 19         32 my $align = $self->{align};
264 19         25 my $align_basename = $self->{align_basename};
265 19         21 my $out_file = $self->{out_file};
266 19         23 my $cytoscape_json = $self->{cytoscape_json};
267 19         24 my $graph_stats = $self->{graph_stats};
268 19         24 my $append_prefixes = $self->{append_prefixes};
269 19         27 my $primary_key = $self->{primary_key};
270 19         20 my $poi = $self->{patients_of_interest};
271 19         30 my $poi_out_dir = $self->{poi_out_dir};
272 19         22 my $cli = $self->{cli};
273 19         22 my $similarity_metric_cohort = $self->{similarity_metric_cohort};
274 19         31 my $weight = undef;
275              
276             # -----------------------------------------------------
277             # Check directories for --align and --export options
278             # -----------------------------------------------------
279 19 100       524 my $align_dir = defined $align ? dirname($align) : '.';
280 19 50       261 die "Directory <$align_dir> does not exist (used with --align)\n"
281             unless -d $align_dir;
282 19 50       46 my $export_dir = defined $export ? dirname($export) : '.';
283 19 50       97 die "Directory <$export_dir> does not exist (used with --export)\n"
284             unless -d $export_dir;
285              
286             # -----------------------------------------------------
287             # Check for precomputed data (glob_hash, ref_hash, ref_binary_hash)
288             # -----------------------------------------------------
289             my $has_precomputed =
290             defined $self->{glob_hash_file}
291             && defined $self->{ref_hash_file}
292 19   66     63 && defined $self->{ref_binary_hash_file};
293              
294 19         32 my ( $glob_hash, $ref_hash, $ref_binary_hash, $hash2serialize );
295              
296 19 100       103 if ($has_precomputed) {
297              
298 1 50       4 say "Using precomputed data" if $self->{verbose};
299              
300             # Use precomputed data provided via Moo attributes
301 1         3 $glob_hash = read_json( $self->{glob_hash_file} );
302 1         3 $ref_hash = read_json( $self->{ref_hash_file} );
303 1         21 $ref_binary_hash = read_json( $self->{ref_binary_hash_file} );
304              
305             # Set format explicitly (for example, to 'PXF')
306 1         5 $self->add_attribute( 'format', 'PXF' );
307              
308 1         3 $hash2serialize = {
309             glob_hash => $glob_hash,
310             ref_hash => $ref_hash,
311             ref_binary_hash => $ref_binary_hash,
312             };
313             }
314             else {
315             # -----------------------------------------------------
316             # Part A: Load reference cohort data
317             # -----------------------------------------------------
318 18         125 my $ref_data =
319             $self->_load_reference_cohort_data( $reference_files, $primary_key,
320             $append_prefixes );
321              
322             # -----------------------------------------------------
323             # Load weights file and HPO data if needed
324             # -----------------------------------------------------
325             # We assing weights if <--w>
326             # NB: The user can exclude variables by using variable: 0
327 18         57 $weight = validate_json($weights_file);
328              
329             # Now we load $hpo_nodes, $hpo_edges if --include_hpo_ascendants
330             # NB: we load them within $self to minimize the #args
331              
332 16 50       60 if ($include_hpo_ascendants) {
333 0         0 my ( $nodes, $edges ) = parse_hpo_json( read_json($hpo_file) );
334 0         0 $self->{nodes} = $nodes;
335 0         0 $self->{edges} = $edges;
336             }
337              
338             # -----------------------------------------------------
339             # Part B: Compute cohort metrics
340             # -----------------------------------------------------
341 16         56 my ( $coverage_stats, $glob_hash_computed, $ref_hash_computed,
342             $ref_binary_hash_computed, $hash2serialize_computed )
343             = $self->_compute_cohort_metrics( $ref_data, $weight, $primary_key,
344             $target_file );
345              
346 15         25 $glob_hash = $glob_hash_computed;
347 15         24 $ref_hash = $ref_hash_computed;
348 15         24 $ref_binary_hash = $ref_binary_hash_computed;
349 15         23201 $hash2serialize = $hash2serialize_computed;
350             }
351              
352             # -----------------------------------------------------
353             # If no target file is provided, perform cohort comparison
354             # -----------------------------------------------------
355 16 100       134 cohort_comparison( $ref_binary_hash, $self ) unless $target_file;
356              
357             # Create and write Cytoscape JSON if requested
358 16         106 my $graph = $self->_perform_graph_calculations( $out_file, $cytoscape_json,
359             $graph_stats, $similarity_metric_cohort );
360              
361             # -----------------------------------------------------
362             # Part C: Process patient data (if target_file is provided)
363             # -----------------------------------------------------
364 16 100       35 if ($target_file) {
365             $self->_process_patient_data(
366             {
367             target_file => $target_file,
368             primary_key => $primary_key,
369             weight => $weight,
370             glob_hash => $glob_hash,
371             ref_hash => $ref_hash,
372             ref_binary_hash => $ref_binary_hash,
373             align => $align,
374             align_basename => $align_basename,
375             out_file => $out_file,
376             cli => $cli,
377             verbose => $self->{verbose},
378             },
379 2         50 \$hash2serialize
380             );
381             }
382              
383             # -----------------------------------------------------
384             # Export JSON if requested
385             # -----------------------------------------------------
386 16 50       50 if ( defined $export ) {
387 0 0       0 serialize_hashes(
388             {
389             data => $hash2serialize,
390             export_basename => $export ? $export : $export_basename
391             }
392             );
393             }
394              
395 16         4340 return 1;
396             }
397              
398             # ============================================================
399             # Private method: _load_reference_cohort_data
400             # ------------------------------------------------------------
401             # Loads each reference cohort file, validates the primary_key,
402             # and then appends prefixes if needed.
403             # ============================================================
404             sub _load_reference_cohort_data {
405 18     18   55 my ( $self, $reference_files, $primary_key, $append_prefixes ) = @_;
406              
407             # *** IMPORTANT ***
408             # $ref_data is an array array where each element is the content of the file (e.g, [] or {})
409              
410 18         29 my $ref_data = [];
411 18         20 for my $cohort_file ( @{$reference_files} ) {
  18         37  
412 16 50       165 die "<$cohort_file> does not exist\n" unless -f $cohort_file;
413 16         95 my $json_data = io_yaml_or_json(
414             {
415             filepath => $cohort_file,
416             mode => 'read'
417             }
418             );
419              
420             # Check for existence of primary_key otherwise die
421             # Expected cases:
422             # - A) BFF/PXF (default config) exists primary_key('id')
423             # - B) JSON (default config) exists primary_key('id') - i.e., OpenEHR
424             # - C) JSON (external config) exists primary_key
425              
426 16         62 my $msg =
427             "Sorry, <$cohort_file> does not contain primary_key <$primary_key>. Are you using the right configuration file?\n";
428 16 100       54 if ( ref $json_data eq ref [] ) {
429 15 50       62 die $msg unless exists $json_data->[0]->{$primary_key};
430             }
431             else {
432 1 50       5 die $msg unless exists $json_data->{$primary_key};
433             }
434 16         54 push @$ref_data, $json_data;
435             }
436              
437             # In we join --cohorts into one but we rename the values of primary_key
438             # NB: Re-using $ref_data to save memory
439              
440 18         165 $ref_data = append_and_rename_primary_key(
441             {
442             ref_data => $ref_data,
443             append_prefixes => $append_prefixes,
444             primary_key => $primary_key
445             }
446             );
447 18         50 return $ref_data;
448             }
449              
450             # ============================================================
451             # Private method: _compute_cohort_metrics
452             # ------------------------------------------------------------
453             # Computes cohort coverage statistics, restructures the data
454             # (e.g. PXF interpretations), and then creates the global and
455             # per-individual hashes along with their one-hot encoded version.
456             # ============================================================
457             sub _compute_cohort_metrics {
458 16     16   43 my ( $self, $ref_data, $weight, $primary_key, $target_file ) = @_;
459 16         39 my $coverage_stats = coverage_stats($ref_data);
460             die
461 1         13 "--include-terms <@{$self->{include_terms}}> does not exist in the cohort(s)\n"
462             unless check_existence_of_include_terms( $coverage_stats,
463 16 100       53 $self->{include_terms} );
464              
465             # We have to check if we have BFF|PXF or others (unless defined at config)
466              
467             $self->add_attribute( 'format', check_format($ref_data) )
468 15 100       92 unless defined $self->{format};
469 15         52 restructure_pxf_interpretations( $ref_data, $self );
470              
471             # First we create:
472             # - $glob_hash => hash with all the COHORT keys possible
473             # - $ref_hash => BIG hash with all individiduals' keys "flattened"
474              
475 15         56 my ( $glob_hash, $ref_hash ) =
476             create_glob_and_ref_hashes( $ref_data, $weight, $self );
477              
478             # Limit the number of variables if > $self-{max_number_vars}
479             # *** IMPORTANT ***
480             # Change only performed in $glob_hash
481 15 50       85 if ( keys %$glob_hash > $self->{max_number_vars} ) {
482 0         0 $glob_hash = randomize_variables( $glob_hash, $self );
483             }
484              
485             # Second we peform one-hot encoding for each individual
486 15         49 my $ref_binary_hash = create_binary_digit_string( $glob_hash, $ref_hash );
487              
488             # Hashes to be serialized to JSON if <--export>
489 15         92 my $hash2serialize = {
490             glob_hash => $glob_hash,
491             ref_hash => $ref_hash,
492             ref_binary_hash => $ref_binary_hash,
493             coverage_stats => $coverage_stats
494             };
495             return (
496 15         59 $coverage_stats, $glob_hash, $ref_hash,
497             $ref_binary_hash, $hash2serialize
498             );
499             }
500              
501             # ============================================================
502             # Private method: _process_patient_data
503             # ------------------------------------------------------------
504             # Loads patient data from the target file, validates it,
505             # restructures interpretations, and then performs the patient
506             # to cohort comparison and ranking.
507             # ============================================================
508             sub _process_patient_data {
509 2     2   6 my ( $self, $params, $hash2serialize_ref ) = @_;
510 2         4 my $target_file = $params->{target_file};
511 2         4 my $primary_key = $params->{primary_key};
512 2         5 my $weight = $params->{weight};
513 2         4 my $glob_hash = $params->{glob_hash};
514 2         2 my $ref_hash = $params->{ref_hash};
515 2         4 my $ref_binary_hash = $params->{ref_binary_hash};
516 2         3 my $align = $params->{align};
517 2         3 my $align_basename = $params->{align_basename};
518 2         3 my $out_file = $params->{out_file};
519 2         4 my $cli = $params->{cli};
520 2         2 my $verbose = $params->{verbose};
521              
522 2         18 my $tar_data = array2object(
523             io_yaml_or_json( { filepath => $target_file, mode => 'read' } ) );
524              
525             # The target file has to have $_->{$primary_key} otherwise die
526              
527             die
528             "Sorry, <$target_file> does not contain primary_key <$primary_key>. Are you using the right config file?\n"
529 2 50       8 unless exists $tar_data->{$primary_key};
530 2         8 restructure_pxf_interpretations( $tar_data, $self );
531              
532             # We store {primary_key} as a variable as it might be deleted from $tar_data (--exclude-terms id)
533              
534 2         4 my $tar_data_id = $tar_data->{$primary_key};
535 2         15 my $tar_hash = {
536             $tar_data_id => remap_hash(
537             {
538             hash => $tar_data,
539             weight => $weight,
540             self => $self
541             }
542             )
543             };
544              
545             # *** IMPORTANT ***
546             # The target binary is created from matches to $glob_hash
547             # Thus, it does not include variables ONLY present in TARGET
548              
549 2         13 my $tar_binary_hash = create_binary_digit_string( $glob_hash, $tar_hash );
550             my (
551 2         16 $results_rank, $results_align, $alignment_ascii,
552             $alignment_dataframe, $alignment_csv
553             )
554             = compare_and_rank(
555             {
556             glob_hash => $glob_hash,
557             ref_hash => $ref_hash,
558             tar_hash => $tar_hash,
559             ref_binary_hash => $ref_binary_hash,
560             tar_binary_hash => $tar_binary_hash,
561             weight => $weight,
562             self => $self
563             }
564             );
565 2 50       12 say join "\n", @$results_rank if $cli;
566 2         22 write_array2txt( { filepath => $out_file, data => $results_rank } );
567              
568 2 50       12 if ( defined $align ) {
569 2 50       19 write_alignment(
570             {
571             align => $align ? $align : $align_basename,
572             ascii => $alignment_ascii,
573             dataframe => $alignment_dataframe,
574             csv => $alignment_csv
575             }
576             );
577             }
578 2         14 $$hash2serialize_ref->{tar_hash} = $tar_hash;
579 2         7 $$hash2serialize_ref->{tar_binary_hash} = $tar_binary_hash;
580 2 50       12 $$hash2serialize_ref->{alignment_hash} = $results_align if defined $align;
581 2         1766 return 1;
582             }
583              
584             sub _perform_graph_calculations {
585 16     16   60 my ( $self, $out_file, $cytoscape_json, $graph_stats,
586             $similarity_metric_cohort )
587             = @_;
588              
589 16         24 my $graph;
590 16 100       36 if ($cytoscape_json) {
591             $graph = matrix2graph(
592             {
593             matrix => $out_file,
594             json => $cytoscape_json,
595             graph_stats => 1,
596             verbose => $self->{verbose},
597             }
598 1         12 );
599             }
600              
601 16 100       39 if ( defined $graph_stats ) {
602             cytoscape2graph(
603             {
604             graph => $graph,
605             output => $graph_stats,
606             metric => $similarity_metric_cohort,
607             verbose => $self->{verbose},
608             }
609 1         8 );
610             }
611              
612 16         48 return $graph;
613             }
614              
615             sub add_attribute {
616 13     13 0 29 my ( $self, $name, $value ) = @_;
617 13         26 $self->{$name} = $value;
618 13         15 return 1;
619             }
620              
621             1;
622              
623             =pod
624              
625             =head1 NAME
626              
627             Convert::Pheno - A module that performs semantic similarity in PXF/BFF data structures and beyond (JSON|YAML)
628            
629             =head1 SYNOPSIS
630              
631             use Pheno::Ranker;
632              
633             # Create object
634             my $ranker = Pheno::Ranker->new(
635             {
636             reference_files => ['individuals.json'],
637             out_file => 'matrix.txt'
638             }
639             );
640              
641             # Run it (output are text files)
642             $ranker->run;
643              
644             =head1 DESCRIPTION
645              
646             We recommend using the included L.
647              
648             For a better description, please read the following documentation:
649              
650             =over
651              
652             =item General:
653              
654             L
655              
656             =item Command-Line Interface:
657              
658             L
659              
660             =back
661              
662             =head1 CITATION
663              
664             The author requests that any published work that utilizes C includes a cite to the the following reference:
665              
666             Leist, I.C. et al. "Advancing Semantic Similarity Analysis of Phenotypic Data Stored in GA4GH Standards and Beyond. (2024) I.
667              
668             =head1 AUTHOR
669              
670             Written by Manuel Rueda, PhD. Info about CNAG can be found at L.
671              
672             =head1 METHODS
673              
674             There is only method named C. See above the syntax.
675              
676             For more information check the documentation:
677              
678             L
679              
680             =head1 COPYRIGHT
681              
682             This PERL file is copyrighted. See the LICENSE file included in this distribution.
683              
684             =cut