1 This file contains the code
for the subroutines
in
2 Perl
-speaks
-NONMEMs data module
. It is
not functional by itself
.
3 The code should be transferred to the module autogenerated by dia2code
4 using the fill_diacode
.pl script
.
8 start include statements
9 use Digest
::MD5
'md5_hex';
21 use Time
::HiRes
qw(gettimeofday);
22 my @primary_column_names = ('ID', 'DATE', 'DAT1', 'DAT2', 'DAT3' ,'L1', 'L2', 'DV', 'MDV', 'RAW_', 'MRG_', 'RPT_', 'TIME', 'DROP', 'SKIP', 'EVID', 'AMT', 'RATE', 'SS', 'II', 'ADDL', 'CMT', 'PCMT', 'CALL');
25 # }}} include statements
30 # The structure of the data class is subject-centric, recognising that
31 # the subjects included in a study often can be regarded as
32 # independent. A class for the subject level exists within PsN and is
33 # called the individual class. A data object consists of at least one
34 # but probably many individual objects plus optional comments.
44 # my $data_obj = data -> new ( filename => 'test040314.dta' );
46 # $data_obj -> renumber_ascending;
48 # my $subsets_ref = $data_obj -> case_deletion( bins => 10 );
50 # my @subsets = @{$subsets_ref};
60 # <a HREF="model.html">model</a>, <a HREF="output.html">output</a>,
61 # <a HREF="tool/modelfit.html">tool::modelfit</a>,
62 # <a HREF="tool.html">tool</a>
68 # model, output, tool::modelfit, tool
79 # If the column holding the subject identifier is not the
80 # first, it can be specified using the I<idcolumn> attribute
82 # I<ignoresign> determines which rows that are regarded as
83 # comments. Corresponds to the IGNORE= option in the $DATA
84 # record in a NONMEM model file.
86 $this -> {'use_data_table'} = 0;
88 ( $this -> {'directory'},
89 $this -> {'filename'} ) = OSspecific
::absolute_path
( $this -> {'directory'},
90 $this->{'filename'} );
92 debug
-> warn( level
=> 2,
93 message
=> "data -> new: Data object initialized from file: ".
96 # sub register_in_database {
98 # # Backslashes messes up the sql syntax
99 # my $file_str = $this->{'filename'};
100 # my $dir_str = $this->{'directory'};
101 # $file_str =~ s/\\/\//g;
102 # $dir_str =~ s/\\/\//g;
105 # my $md5sum = md5_hex(OSspecific::slurp_file($this-> full_name ));
107 # connect("DBI:mysql:host=".$PsN::config -> {'_'} -> {'database_server'}.
108 # ";databse=".$PsN::config -> {'_'} -> {'project'},
109 # $PsN::config -> {'_'} -> {'user'},
110 # $PsN::config -> {'_'} -> {'password'},
111 # {'RaiseError' => 1});
113 # my $sth = $dbh -> prepare( "SELECT data_id FROM ".$PsN::config -> {'_'} -> {'project'}.
115 # "WHERE filename = '$file_str' AND ".
116 # "directory = '$dir_str' AND ".
117 # "md5sum = '".$md5sum."'" );
118 # $sth -> execute or debug -> die( message => $sth->errstr ) ;
119 # my $select_arr = $sth -> fetchall_arrayref;
120 # if ( scalar @{$select_arr} > 0 ) {
121 # debug -> warn( level => 1,
122 # message => "Found an old entry in the database matching the ".
123 # "current data file" );
124 # if ( scalar @{$select_arr} > 1 ) {
125 # debug -> warn( level => 1,
126 # message => "Found more than one matching entry in database".
127 # ", using the first" );
129 # $this -> {'data_id'} = $select_arr->[0][0];
131 # my ( $date_str, $time_str );
132 # if ( $Config{osname} eq 'MSWin32' ) {
133 # $date_str = `date /T`;
134 # $time_str = ' '.`time /T`;
137 # $date_str = `date`;
141 # my $date_time = $date_str.$time_str;
142 # $sth = $dbh -> prepare("INSERT INTO ".$PsN::config -> {'_'} -> {'project'}.
143 # ".data (filename,date,directory,md5sum) ".
144 # "VALUES ('$file_str', '$date_time', '$dir_str','".
147 # $this -> {'data_id'} = $sth->{'mysql_insertid'};
150 # $dbh -> disconnect;
154 unless ( ( defined $this -> {'header'} and
155 scalar @
{$this -> {'header'}} > 0 ) or
156 ( defined $this -> {'individuals'} and
157 scalar @
{$this -> {'individuals'}} > 0 ) ) {
158 if ( -e
$this -> full_name
) {
159 if ( $this -> {'target'} eq 'mem' ) {
160 # ®ister_in_database( $this ) if ( $PsN::config -> {'_'} -> {'use_database'} and
161 # $this -> {'use_data_table'} );
162 $this -> _read_header
;
163 $this -> _read_individuals
;
164 $this -> {'synced'} = 1;
166 $this -> {'synced'} = 0;
169 debug
-> die(message
=> "No header, individuals, and no file " . $this -> full_name
. " on disk.")
170 unless $this -> {'ignore_missing_files'};
171 $this -> {'synced'} = 0;
174 if ( $this -> {'target'} eq 'mem') {
175 if ( -e
$this -> {'filename'} ) {
176 $this -> _read_header
;
177 # ®ister_in_database if ( $PsN::config -> {'_'} -> {'use_database'} and
178 # $this -> {'use_data_table'} );
179 $this -> _read_individuals
;
180 $this -> {'synced'} = 1;
182 debug
-> die(message
=> "No file:".$this->{'filename'}." on disk" )
183 unless $this -> {'ignore_missing_files'};
184 $this -> {'synced'} = 0;
191 if ( $this -> {'synced'} ) {
193 foreach my $head ( @
{$this -> {'header'}} ) {
194 $this -> {'column_head_indices'} -> {$head} = $i;
198 # $Data::Dumper::Maxdepth = 3;
199 # die Dumper $this -> {'individuals'};
205 # {{{ register_in_database
206 start register_in_database
207 if ( $PsN::config
-> {'_'} -> {'use_database'} ) {
208 # Backslashes messes up the sql syntax
209 my $file_str = $self->{'filename'};
210 my $dir_str = $self->{'directory'};
211 $file_str =~ s/\\/\//g
;
212 $dir_str =~ s/\\/\//g
;
214 my $project = $PsN::config
-> {'_'} -> {'project'};
216 my $md5sum = md5_hex
(OSspecific
::slurp_file
($self-> full_name
));
218 my $dbh = DBI
-> connect("DBI:mysql:host=".$PsN::config
-> {'_'} -> {'database_server'}.
219 ";databse=".$project,
220 $PsN::config
-> {'_'} -> {'user'},
221 $PsN::config
-> {'_'} -> {'password'},
222 {'RaiseError' => 1});
229 my $sth = $dbh -> prepare
( "SELECT data_id FROM ".$project.
231 "WHERE filename = '$file_str' AND ".
232 "directory = '$dir_str' AND ".
233 "md5sum = '".$md5sum."'" );
234 $sth -> execute
or debug
-> die( message
=> $sth->errstr ) ;
235 $select_arr = $sth -> fetchall_arrayref
;
238 if ( scalar @
{$select_arr} > 0 ) {
239 'debug' -> warn( level
=> 1,
240 message
=> "Found an old entry in the database matching the ".
241 "current data file" );
242 if ( scalar @
{$select_arr} > 1 ) {
243 'debug' -> warn( level
=> 1,
244 message
=> "Found more than one data matching entry in database".
245 ", using the first" );
247 $self -> {'data_id'} = $select_arr->[0][0];
249 my $sth = $dbh -> prepare
( "SELECT individual_id FROM ".$project.".data_individual ".
250 "WHERE data_id = '".$self -> {'data_id'}."'" );
251 $sth -> execute
or debug
-> die( message
=> $sth->errstr ) ;
252 my $id_arr = $sth -> fetchall_arrayref
;
253 map( $_ = $_ -> [0], @
{$id_arr} );
254 $self -> {'individual_ids'} = $id_arr;
255 } elsif ( defined $self -> {'individuals'} ) {
256 my ( $date_str, $time_str );
257 if( $Config{osname
} eq 'MSWin32' ){
258 $date_str = `date /T`;
259 $time_str = ' '.`time /T`;
266 my $date_time = $date_str.$time_str;
267 my ( $columns, $values );
268 my $res_str = $resampled ?
'1' : '0';
269 if ( defined $model_id ) {
270 $columns = '(model_id, filename, date, directory, md5sum, resampled)';
271 $values = "('$model_id', '$file_str', '$date_time', '$dir_str','".
272 $md5sum."', '$res_str' )";
274 $columns = '(filename, date, directory, md5sum, resampled)';
275 $values = "('$file_str', '$date_time', '$dir_str','".$md5sum."', '$res_str' )";
277 $sth = $dbh -> prepare
("INSERT INTO ".$PsN::config
-> {'_'} -> {'project'}.
278 ".data $columns VALUES $values");
280 $self -> {'data_id'} = $sth->{'mysql_insertid'};
282 if ( defined $self -> {'data_id'} ) {
284 my $columns = "( id_key, id )";
285 if( $#individual_ids >= 0 ) {
286 $self -> register_di_relation
( individual_ids
=> \
@individual_ids );
288 my $inds = scalar @
{$self -> {'individuals'}};
289 $dbh -> do( "LOCK TABLES ".$PsN::config
-> {'_'} -> {'project'}.
290 ".individual WRITE" );
291 # $sth = $dbh -> prepare( "SELECT MAX(individual_id)".
292 # " FROM ".$PsN::config -> {'_'} -> {'project'}.
294 $dbh -> do( 'USE '.$PsN::config
-> {'_'} -> {'project'} );
295 $sth = $dbh -> prepare
( "SHOW TABLE STATUS LIKE 'individual'" );
296 $sth -> execute
or debug
-> die( message
=> $sth->errstr ) ;
297 my $select_arr = $sth -> fetchall_arrayref
;
298 my $first_id_id = $select_arr -> [0][10] ?
299 $select_arr -> [0][10] : 0;
300 # my $first_id_id = $select_arr -> [0][0] ? ($select_arr -> [0][0] + 1) : 0;
301 my $last_id_id = $first_id_id + $inds - 1;
302 for( my $i = 0; $i < $inds; $i++ ) {
303 if( defined $self -> {'individuals'}[$i] ) {
304 my $id_id = $self -> {'individuals'}[$i] -> idnumber
;
305 $values = $values."," if ( defined $values );
306 $values = $values."( $i, $id_id )";
309 $sth = $dbh -> prepare
( "INSERT INTO ".$PsN::config
-> {'_'} -> {'project'}.
310 ".individual $columns VALUES $values" );
312 $dbh -> do( "UNLOCK TABLES" );
313 @individual_ids = ($first_id_id .. $last_id_id);
314 $self -> register_di_relation
( individual_ids
=> \
@individual_ids );
316 $self -> {'individual_ids'} = \
@individual_ids;
321 $data_id = $self -> {'data_id'}; # return the data_id
323 end register_in_database
324 # }}} register_in_database
326 # {{{ register_di_relation
327 start register_di_relation
328 if ( $PsN::config
-> {'_'} -> {'use_database'} and
329 defined $self -> {'data_id'} and $#individual_ids >= 0 ) {
330 my $dbh = DBI
-> connect("DBI:mysql:host=".$PsN::config
-> {'_'} -> {'database_server'}.
331 ";databse=".$PsN::config
-> {'_'} -> {'project'},
332 $PsN::config
-> {'_'} -> {'user'},
333 $PsN::config
-> {'_'} -> {'password'},
334 {'raiseerror' => 1});
337 my $columns = "( data_id, individual_id )";
338 foreach my $individual_id ( @individual_ids ) {
339 if ( defined $individual_id ) {
340 $values = $values."," if ( defined $values );
341 $values = $values."(".$self -> {'data_id'}.", $individual_id )";
344 $sth = $dbh -> prepare
( "INSERT INTO ".$PsN::config
-> {'_'} -> {'project'}.
345 ".data_individual $columns VALUES $values" );
347 $sth -> finish
if ( defined $sth );
350 end register_di_relation
351 # }}} register_di_relation
357 $full_name = $self -> {'directory'} . $self -> {'filename'};
367 # The bootstrap method draws I<samples> number of boostrap
368 # samples from the data set. The I<subjects> arguments
369 # determines the size of each sample (default equals to the
370 # number of individuals in the original data set). The method
371 # returns references to three arrays: I<boot_samples_ref>,
372 # which holds the bootstrap data sets, I<incl_individuals_ref>
373 # which holds arrays containing the subject identifiers (ID's)
374 # for the included individuals of each bootstrap data set and
375 # I<included_keys_ref> which holds the key or index of the
376 # included individuals. The key or index is an integer
377 # starting at 1 for the first individual in the original data
378 # set and increasing by one for each following.
379 $self -> synchronize
;
380 my @header = @
{$self -> {'header'}};
381 my $individuals = $self -> {'individuals'};
384 my $status_bar = status_bar
-> new
( steps
=> $samples );
385 ui
-> print( category
=> 'bootstrap',
386 message
=> $status_bar -> print_step
,
389 for ( my $i = 1; $i <= $samples; $i++ ) {
390 my $new_name = defined $name_stub ?
$name_stub."_$i.dta" : "bs$i.dta";
391 $new_name = $directory.'/'.$new_name;
392 my ( $boot, $incl_ind_ref, $incl_key_ref ) =
393 $self -> resample
( subjects
=> \
%subjects,
395 new_name
=> $new_name,
397 stratify_on
=> $stratify_on,
398 model_id
=> $model_ids[$i-1] );
399 push( @included_keys, $incl_key_ref );
400 push( @incl_individuals, $incl_ind_ref );
401 # $boot -> renumber_ascending;
402 push( @boot_samples, $boot );
403 # $boot -> synchronize;
405 if( $status_bar -> tick
() ){
406 ui
-> print( category
=> 'bootstrap',
407 message
=> $status_bar -> print_step
,
411 # print Dumper \@boot_samples;
414 ui
-> print( category
=> 'bootstrap',
415 message
=> ' ... done' );
425 $self -> synchronize
;
426 my ( @header, $individuals, @bs_inds, $key_ref, @id_ids, @bs_id_ids );
427 @id_ids = @
{$self -> {'individual_ids'}} if( defined $self -> {'individual_ids'} );
428 my @subj_keys = keys( %subjects );
429 if ( $#subj_keys < 0 ) {
430 debug
-> die( message
=> "sample_size must be defined" );
432 if ( defined $stratify_on ) {
434 if( $stratify_on =~ /\D/ ){
435 %strata = %{$self -> factors
( column_head
=> $stratify_on )};
436 if ( $strata{'Non-unique values found'} eq '1' ) {
437 debug
-> die( message
=> "Individuals were found to have multiple values in the $stratify_on column. ".
438 "The column $stratify_on cannot be used for stratification of the resampling." );
441 %strata = %{$self -> factors
( column
=> $stratify_on )};
442 if ( $strata{'Non-unique values found'} eq '1' ) {
443 debug
-> die( message
=> "Individuals were found to have multiple values in column number $stratify_on. ".
444 "Column $stratify_on cannot be used for stratification of the resampling." );
447 if ( scalar keys( %subjects) != scalar keys( %strata ) and
448 not ( $#subj_keys == 0 and defined $subjects{'default'} ) ) {
449 debug
-> die( message
=> "sample_size must be defined using one default value ".
450 "or exactly one value per strata:\n".
451 "resampling per STUD=1001,1002,1003\n".
452 "use -sample_size='1001=>10,1002=>25,1003=>12' or ".
453 "-sample_size='default=>10'");
455 unless ( $resume and -e
$new_name ) {
456 @header = @
{$self -> {'header'}};
457 $individuals = $self -> {'individuals'};
458 while( my ( $factor, $key_list ) = each %strata ) {
460 if ( defined $subjects{$factor} ) {
461 $keys = $subjects{$factor};
462 } elsif( defined $subjects{'default'} ) {
463 $keys = sprintf( "%.0f",($subjects{'default'}*
464 (scalar(@
{$key_list}))/($self -> count_ind
())) );
466 debug
-> die( message
=> "A sample size for strata $factor could not be found ".
467 "and no default sample size was set" );
469 for ( my $i = 0; $i < $keys; $i++ ) {
470 my $list_ref = random_uniform_integer
(1,0,(scalar(@
{$key_list}) - 1));
471 push( @bs_inds, $individuals ->
472 [ $key_list -> [$list_ref] ] -> copy
);
473 push( @included_keys, $key_list -> [$list_ref] );
474 push( @incl_individuals, $individuals ->
475 [ $key_list -> [$list_ref] ] -> idnumber
);
476 push( @bs_id_ids, $id_ids[ $key_list -> [$list_ref] ] );
480 $boot = data
-> new
( header
=> \
@header,
481 idcolumn
=> $self -> {'idcolumn'},
482 ignoresign
=> $self -> {'ignoresign'},
483 individuals
=> \
@bs_inds,
484 filename
=> $new_name,
485 ignore_missing_files
=> 1,
487 $boot -> renumber_ascending
;
490 #$boot -> target( $target );
492 # If we are resuming, we still need to generate the
493 # pseudo-random sequence and initiate a data object
494 while( my ( $factor, $key_list ) = each %strata ) {
496 if ( defined $subjects{$factor} ) {
497 $keys = $subjects{$factor};
498 } elsif( defined $subjects{'default'} ) {
499 $keys = sprintf( "%.0f",($subjects{'default'}*
500 (scalar(@
{$key_list}))/($self -> count_ind
())) );
502 debug
-> die( message
=> "A sample size for strata $factor could not be found ".
503 "and no default sample size was set" );
505 for ( my $i = 0; $i < $keys; $i++ ) {
506 my $list_ref = random_uniform_integer
(1,0,(scalar(@
{$key_list}) - 1));
509 $boot = data
-> new
( idcolumn
=> $self -> {'idcolumn'},
510 ignoresign
=> $self -> {'ignoresign'},
511 filename
=> $new_name,
512 ignore_missing_files
=> 1,
520 if( defined $subjects{'default'} ) {
521 $size = $subjects{'default'};
523 debug
-> die( message
=> "No default sample size was set" );
525 unless ( $resume and -e
$new_name ) {
526 @header = @
{$self -> {'header'}};
527 $individuals = $self -> {'individuals'};
528 for ( my $i = 1; $i <= $size; $i++ ) {
529 $key_ref = random_uniform_integer
(1,0,scalar @
{$individuals}-1);
530 push( @bs_inds, $individuals -> [ $key_ref ] -> copy
);
531 push( @included_keys, $key_ref );
532 push( @incl_individuals, $individuals -> [ $key_ref ] -> idnumber
);
533 push( @bs_id_ids, $id_ids[ $key_ref ] );
536 # MUST FIX: If a file already exists with the same name,
537 # the created bs data set will be appended to this. IT
538 # MUST BE OVERWRITTEN!
539 $boot = data
-> new
( header
=> \
@header,
540 idcolumn
=> $self -> {'idcolumn'},
541 ignoresign
=> $self -> {'ignoresign'},
542 individuals
=> \
@bs_inds,
543 filename
=> $new_name,
544 ignore_missing_files
=> 1,
546 $boot -> renumber_ascending
;
548 $boot -> target
( $target );
550 # If we are resuming, we still need to generate the
551 # pseudo-random sequence and initiate a data object
552 for ( my $i = 1; $i <= $size; $i++ ) {
553 random_uniform_integer
(1,0,scalar @
{$individuals}-1)
555 $boot = data
-> new
( idcolumn
=> $self -> {'idcolumn'},
556 ignoresign
=> $self -> {'ignoresign'},
557 filename
=> $new_name,
558 ignore_missing_files
=> 1,
564 if( $target eq 'disk'){
568 $boot -> register_in_database
( individual_ids
=> \
@bs_id_ids,
570 model_id
=> $model_id );
580 # case_deletion creates subsets of the data. The number of
581 # subsets is specified by the bins argument. The individuals
582 # of each subset is selected randomly or in ascending
583 # numerical order depending on the selection argument that can
584 # be either 'consecutive' or 'random'. case_column must be
585 # specified to give the method something to base the selection
586 # on. Valid case_column values are either the column number
587 # (pure digits) or the name of the column in the (optional)
589 $self -> synchronize
;
590 my @header = @
{$self -> {'header'}};
591 if ( not defined $case_column ) {
592 debug
-> die( message
=> "case_column must be specified" );
594 if ( not $case_column =~ /^\d/ ) {
595 for ( my $i = 0; $i <= $#header; $i++ ) {
596 $case_column = $i+1 if ( $header[$i] eq $case_column );
600 $bins = defined $bins ?
$bins :
601 scalar keys %{$self -> factors
( column
=> $case_column)};
602 my %factors = %{$self -> factors
( column
=> $case_column )};
603 if ( $factors{'Non-unique values found'} eq '1' ) {
604 debug
-> die( message
=> "Individuals were found to have multiple values in column number $case_column. ".
605 "Column $case_column cannot be used for case deletion." );
608 my $maxbins = scalar keys %factors;
609 my @ftrs = sort { $a <=> $b } keys %factors;
610 my $individuals = $self -> {'individuals'};
611 my $maxkey = scalar @
{$individuals} - 1;
613 my ( @tmp_ftrs, @binsize ) =
615 my ( $k, $j, $i ) = ( 0, 0, 0 );
616 # Create the binsizes
617 for ( $j = 0; $j < $maxbins; $j++ ) {
619 $k = 0 if( $k >= $bins );
621 $self -> _fisher_yates_shuffle
( array
=> \
@ftrs ) if( $selection eq 'random' );
622 for ( $k = 0; $k < $bins; $k++ ) {
623 for ( $j = 0; $j < $binsize[ $k ]; $j++ ) {
624 # print "SK: ",$skipped_keys[ $k ]," F: ",$factors{ $ftrs[ $i ] },"\n";
625 push( @
{$skipped_keys[ $k ]}, @
{$factors{ $ftrs[ $i ] }} );
626 push( @
{$skipped_values[ $k ]}, $ftrs[ $i++ ] );
630 for ( $k = 0; $k < $bins; $k++ ) {
633 SELKEYS
: foreach my $key ( 0..$maxkey ) {
634 foreach my $skipped ( @
{$skipped_keys[ $k ]} ) {
635 if ( $key == $skipped ) {
636 push( @
{$skipped_ids[ $k ]}, $individuals ->
637 [ $skipped ] -> idnumber
);
638 push( @del_inds, $individuals -> [ $key ] -> copy
);
642 push( @cd_inds, $individuals -> [ $key ] -> copy
);
644 # Set ignore_missing_files = 1 to make it possible to get the result
646 my $newdata = data
->
647 new
( header
=> \
@header,
648 ignoresign
=> $self -> {'ignoresign'},
649 idcolumn
=> $self -> {'idcolumn'},
650 individuals
=> \
@cd_inds,
652 filename
=> $directory.'/cdd_'.($k+1).'.dta',
653 ignore_missing_files
=> 1 );
654 my $deldata = data
->
655 new
( header
=> \
@header,
656 ignoresign
=> $self -> {'ignoresign'},
657 idcolumn
=> $self -> {'idcolumn'},
658 individuals
=> \
@del_inds,
660 filename
=> $directory.'/rem_'.($k+1).'.dta',
661 ignore_missing_files
=> 1 );
662 push( @subsets, $newdata );
663 push( @remainders, $deldata );
677 # filename: new data file name.
679 # target: keep the copy in memory ('mem') or write it to disk and flush the memory ('disk').
681 ($directory, $filename) = OSspecific
::absolute_path
( $directory, $filename );
683 # Clone self into new data object. Why don't the individuals get cloned too?
684 # strange. need to set synced to 0 AND set the {'individuals'} to undef.
685 cp
($self -> full_name
, $directory.$filename );
686 $new_data = Storable
::dclone
( $self );
687 $new_data -> {'synced'} = 0;
688 $new_data -> {'individuals'} = undef;
689 $new_data -> synchronize
;
691 # Set the new file name for the copy
692 $new_data -> directory
( $directory );
693 $new_data -> filename
( $filename );
699 # {{{ column_to_array
700 start column_to_array
702 $self -> synchronize
;
704 if ( not $column =~ /^\d/ ) {
705 $column = $self -> {'column_head_indices'} -> {$column} - 1;
708 if( $column < 0 or $column > $#{$self -> {'header'}} ){
712 foreach my $individual ( @
{$self -> individuals
} ){
713 foreach my $individual_row( @
{$individual -> subject_data
} ){
714 my @row = split(/,/ , $individual_row);
715 push( @array, $row[$column] );
726 # Returns the number of individuals in the data set.
727 $self -> synchronize
;
728 $num = scalar @
{$self -> {'individuals'}};
737 $self -> synchronize
;
739 my $first_id = $self -> {'individuals'}[0];
741 debug
-> die( message
=> "No individuals defined in data object based on ".
742 $self -> full_name
) unless ( defined $first_id );
744 # Check if $column(-index) is defined and valid, else try to find index
747 my @data_row = split( /,/, $first_id -> subject_data
-> [0] );
748 if( $#columns >= 0 ) {
749 foreach my $column ( @columns ) {
750 unless ( defined $column && defined( $data_row[$column-1] ) ) {
751 debug
-> die( message
=> "Error in data -> factors: ".
752 "invalid column number: \"$column\"\n".
753 "Valid column numbers are 1 to ".
754 scalar @
{$first_id -> subject_data
->[0]}."\n" );
757 } elsif ( $#column_heads >= 0 ) {
758 foreach my $column_head ( @column_heads ) {
759 unless (defined($column_head) && defined($self -> {'column_head_indices'}{$column_head})) {
760 debug
-> die( message
=> "Error in data -> factors: unknown column: \"$column_head\" ".
761 "Valid column headers are (in no particular order):\n".
762 join(', ',keys(%{$self -> {'column_head_indices'}})) );
764 my $column = $self -> {'column_head_indices'}{$column_head};
765 push( @columns, $column );
766 debug
-> warn( level
=> 2,
767 message
=> "$column_head is in column number $column" );
771 debug
-> die( message
=> "No column or column_head defined" );
774 if( $global_largest or $global_smallest or
775 $largest_per_individual or $smallest_per_individual ) {
776 if( not scalar @
{$self -> {'individuals'}} == scalar @
{$against_data -> individuals
} ) {
777 debug
-> die( message
=> "Both data object must hold the same number of individuals ".
778 "and observations when calling data -> diff" );
780 for( my $i = 0; $i < scalar @
{$self -> {'individuals'}}; $i++ ) {
781 my %id_diffs = %{$self -> {'individuals'}[$i] ->
782 diff
( against_individual
=> $against_data -> individuals
-> [$i],
783 columns
=> \
@columns,
784 absolute_diff
=> $absolute_diff,
785 diff_as_fraction
=> $diff_as_fraction,
786 largest
=> ( $global_largest or $largest_per_individual ),
787 smallest
=> ( $global_smallest or $smallest_per_individual ) )};
788 if( $global_largest ) {
789 for( my $j = 0; $j <= $#columns; $j++ ) {
790 my $label = defined $column_heads[$j] ?
$column_heads[$j] : $columns[$j];
791 if( not defined $diff_results{$label} or not defined $diff_results{$label}{'diff'} or
792 $id_diffs{$columns[$j]}{'diff'} > $diff_results{$label}{'diff'} ) {
793 $diff_results{$label}{'diff'} = $id_diffs{$columns[$j]}{'diff'};
794 $diff_results{$label}{'self'} = $id_diffs{$columns[$j]}{'self'};
795 $diff_results{$label}{'test'} = $id_diffs{$columns[$j]}{'test'};
801 die "data -> diff is only implemented for finding the largest difference at any observation at this point\n";
810 if ( defined $parm and $parm ne $self -> {'filename'} ) {
811 $self -> {'filename'} = $parm;
812 $self -> {'data_id'} = undef;
823 my %factors = $self -> factors
( 'return_occurences' => 1,
824 'unique_in_individual' => $unique_in_individual,
825 'column_head' => $column_head,
826 'column' => $column);
829 while (my ($factor, $amount) = each %factors) {
830 if ( $factor == $self -> {'missing_data'} && $ignore_missing ) {
836 while (my ($factor, $amount) = each %factors) {
837 if ( $factor == $self -> {'missing_data'} && $ignore_missing ) {
840 $fractions{$factor} = $amount/$sum;
852 # Either column (number, starting at 1) or column_head must be specified.
854 # The default behaviour is to return a hash with the factors as keys
855 # and as values references to arrays with the order numbers (not the ID numbers)
856 # of the individuals that contain this factor
858 # If unique_in_individual is true (1), the returned hash will contain
859 # an element with key 'Non-unique values found' and value 1 if any
860 # individual contain more than one value in the specified column.
862 # Return occurences will calculate the occurence of each
863 # factor value. Several occurences in one individual counts as
864 # one occurence. The elements of the returned hash will have the factors
865 # as keys and the number of occurences as values.
868 $self -> synchronize
;
870 # Check if $column(-index) is defined and valid, else try to find index
872 my $first_id = $self -> {'individuals'}[0];
874 debug
-> die( message
=> "No individuals defined in data object based on ".
875 $self -> full_name
) unless ( defined $first_id );
877 my @data_row = split( /,/, $first_id -> subject_data
-> [0] );
878 unless ( defined $column && defined( $data_row[$column-1] ) ) {
879 unless (defined($column_head) && defined($self -> {'column_head_indices'}{$column_head})) {
880 debug
-> die( message
=> "Error in data -> factors: unknown column: \"$column_head\" ".
881 "or invalid column number: \"$column\".\n".
882 "Valid column numbers are 1 to ".scalar @data_row ."\n".
883 "Valid column headers are (in no particular order):\n".
884 join(', ',keys(%{$self -> {'column_head_indices'}})) );
886 $column = $self -> {'column_head_indices'}{$column_head};
887 debug
-> warn( level
=> 2,
888 message
=> "$column_head is in column number $column" );
893 foreach my $individual ( @
{$self -> {'individuals'}} ) {
894 my @ifactors = keys %{$individual -> factors
( column
=> $column )};
895 if ( scalar @ifactors > 1 and $unique_in_individual ) {
896 %factors = ( 'Non-unique values found' => 1 );
899 debug
-> die( message
=> "No value found in column $column in individual ".
900 $individual -> idnumber
) if ( scalar @ifactors == 0 );
902 # Return occurences will calculate the occurence of each
903 # factor value. Several occurences in one individual counts as
906 if ( $return_occurences ) {
907 foreach my $ifactor ( @ifactors ) {
908 $factors{$ifactor}++;
911 foreach my $ifactor ( @ifactors ) {
912 push( @
{$factors{$ifactor}}, $key );
922 # {{{ find_individual
924 # start find_individual
925 # foreach my $tmp_ind ( @{$self -> individuals} ) {
926 # if ( $tmp_ind -> key == $key ) {
927 # $individual = $tmp_ind;
931 # if ( defined $individual ) {
933 # $individual = $individual -> copy;
936 # print "No individual with key $key found in call to ".
937 # "data -> find_individual\n" if ( $self -> debug );
939 # end find_individual
947 my $header = $self -> {'header'};
949 # format the data for NONMEM (simple comma-separated layout)
950 if ( defined $self -> {'comment'} ) {
951 my @comment = @
{$self -> {'comment'}};
957 my $wrap = ( defined $self -> {'wrap_column'} and
958 defined $self -> {'cont_column'} );
960 my @primary_columns = defined $self -> {'primary_columns'} ?
961 @
{$self -> {'primary_columns'}} : ();
962 my @secondary_columns = defined $self -> {'secondary_columns'} ?
963 @
{$self -> {'secondary_columns'}} : ();
964 if ( defined $header and defined $self -> {'ignoresign'} ) {
966 if ( $self -> {'ignoresign'} ne '@' ) {
967 $istr = $self -> {'ignoresign'};
971 for ( my $i = 0; $i <= $#secondary_columns ; $i++ ) {
973 for ( my $j = 0; $j < scalar @
{$secondary_columns[$i]} ; $j++ ) {
974 my $jstr = $j == 0 ?
'' : ',';
975 $sstr = $sstr.$jstr.$secondary_columns[$i][$j][0];
977 push( @h_data, $sstr."\n" );
979 push( @form_data, @h_data );
981 for ( my $i = 0; $i <= $#primary_columns ; $i++ ) {
982 my $jstr = $i == 0 ?
'' : ',';
983 $pstr = $pstr.$jstr.$primary_columns[$i][0];
985 push( @form_data, $pstr."\n" );
987 push( @form_data, $istr.join(',',@
{$self -> {'header'}})."\n" );
991 foreach my $individual ( @
{$self -> {'individuals'}} ) {
992 foreach my $row ( @
{$individual -> subject_data
} ) {
994 for ( my $i = 0; $i <= $#secondary_columns ; $i++ ) {
996 for ( my $j = 0; $j < scalar @
{$secondary_columns[$i]} ; $j++ ) {
997 my $jstr = $j == 0 ?
'' : ',';
998 if ( $secondary_columns[$i][$j][0] eq 'CONT' ) {
999 $sstr = $sstr.$jstr.'1';
1001 my @data_row = split( /,/, $row );
1002 $sstr = $sstr.$jstr.$data_row[$secondary_columns[$i][$j][1]];
1005 push( @r_data, $sstr."\n" );
1007 push( @form_data, @r_data );
1009 for ( my $i = 0; $i <= $#primary_columns ; $i++ ) {
1010 my $jstr = $i == 0 ?
'' : ',';
1011 if ( $primary_columns[$i][0] eq 'CONT' ) {
1012 $pstr = $pstr.$jstr.'0';
1014 my @data_row = split( /,/, $row );
1015 $pstr = $pstr.$jstr.$data_row[$primary_columns[$i][1]];
1018 push( @form_data, $pstr."\n" );
1022 foreach my $individual ( @
{$self -> {'individuals'}} ) {
1023 foreach my $row ( @
{$individual -> subject_data
} ) {
1024 push( @form_data, $row ."\n" );
1037 # This method removes columns that has '=DROP' value in the
1038 # model header as given by $INPUT. The model header must be
1039 # transfered to this method through the model_header
1040 # argument. The model_header argument should be a
1041 # two-dimensional array where each position in the first
1042 # dimension should be a reference to a 1*2 array holding the
1043 # column name and value. Any ignore-sign must be removed.
1045 debug
-> die( message
=> 'model header must be defined' )
1046 if ( $#model_header < 0 );
1047 # Important that the drop_dropped method of the model::problem
1048 # class is in sync with this method.
1049 $self -> synchronize
;
1051 $self -> {'header'} = [];
1054 for( my $i = 0; $i <= $#model_header; $i++ ) {
1055 $self -> {'idcolumn'} = $counter if ( $model_header[$i][0] eq 'ID' );
1056 if( ( $model_header[$i][1] eq 'DROP' or
1057 $model_header[$i][1] eq 'SKIP' ) and
1058 not $model_header[$i][0] =~ /DAT(E|1|2|3)/ ) {
1063 push( @
{$self -> {'header'}}, $model_header[$i][0] );
1067 foreach my $individual ( @
{$self -> {'individuals'}} ) {
1068 $individual -> drop_columns
( drop
=> \
@drop );
1071 $self -> {'synced'} = 0;
1072 # $Data::Dumper::Maxdepth = 2;
1074 # die Dumper $self -> {'individuals'};
1083 $self -> synchronize
;
1084 $self -> cont_column
( $cont_column ) if ( defined $cont_column );
1085 $self -> wrap_column
( $wrap_column ) if ( defined $wrap_column );
1086 $self -> prepare_wrap
( model_header
=> \
@model_header );
1087 @secondary_columns = @
{$self -> {'secondary_columns'}}
1088 if ( defined $self -> {'secondary_columns'} );
1089 @primary_columns = @
{$self -> {'primary_columns'}}
1090 if ( defined $self -> {'primary_columns'} );
1098 $self -> {'cont_column'} = undef;
1099 $self -> {'wrap_column'} = undef;
1100 $self -> {'secondary_columns'} = undef;
1101 $self -> {'primary_columns'} = undef;
1110 my $cont_column = $self -> {'cont_column'};
1111 my $wrap_column = $self -> {'wrap_column'};
1112 debug
-> die( message
=> 'cont_column ('.$cont_column.') must be less or equal '.
1113 'to the requested number of columns in each row ('.
1114 ($wrap_column).')' )
1115 if ( $cont_column > $wrap_column );
1117 if ( scalar @model_header > 0 ) {
1118 @header = @model_header;
1120 @header = @
{$self -> {'header'}};
1123 my ( @primary, @secondary, @date_columns );
1125 for ( my $i = 0; $i <= $#header; $i++ ) {
1126 my $name = ref( $header[$i] ) eq 'ARRAY' ?
$header[$i][0] : $header[$i];
1127 my $value = ref( $header[$i] ) eq 'ARRAY' ?
$header[$i][1] : undef;
1128 next if ( $name eq 'ID' );
1130 foreach my $prim ( @primary_column_names ) {
1132 ( $name eq $prim or $value eq $prim ) ) {
1133 push( @primary, [$name, $i, $value] );
1135 my $col = ($#primary+2)>= $cont_column ?
($#primary+3) : ($#primary+2);
1136 push( @date_columns, $col ) if ( $name =~ /DAT(E|1|2|3)/ );
1139 push( @secondary, [$name, $i, $value] ) if ( not $found );
1142 my $prim_num = scalar @primary;
1143 debug
-> die( message
=> 'The number of primary columns (that need to '.
1144 'be part of the row with CONT=0) ('.($prim_num+1).
1145 ') is larger than the required number of columns (wrap_column='.
1146 $wrap_column.') - 1' )
1147 if ( scalar $prim_num > ($wrap_column-2) );
1149 my ( $i, $dum ) = ( 0, 1 );
1151 for ( my $j = 1; $j <= $wrap_column; $j++ ) {
1153 push( @tmp, ['ID', $self -> {'idcolumn'}-1] );
1154 } elsif ( $j == $wrap_column ) {
1155 if ( $j == $cont_column ) {
1156 push( @tmp, ['CONT', undef] );
1159 if ( defined $primary[$i] ) {
1160 $val = $primary[$i];
1161 } elsif ( defined $secondary[0] ) {
1162 $val = shift(@secondary);
1164 $val = ['XX'.$dum++,$self -> {'idcolumn'}-1];
1169 push( @
{$self -> {'primary_columns'}}, @tmp );
1171 if ( $j == $cont_column ) {
1172 push( @tmp, ['CONT', undef] );
1174 if ( $i <= $#primary ) {
1175 push( @tmp, $primary[$i] );
1178 my $val = defined $secondary[0] ?
shift(@secondary) :
1179 ['XX'.$dum++,$self -> {'idcolumn'}-1];
1187 while ( $i <= $#secondary ) {
1189 for ( my $j = 1; $j <= $wrap_column; $j++ ) {
1191 push( @tmp, ['ID', $self -> {'idcolumn'}-1] );
1192 } elsif ( $j == $wrap_column ) {
1193 if ( $j == $cont_column ) {
1194 push( @tmp, ['CONT', undef] );
1196 my $val = defined $secondary[$i] ?
$secondary[$i] :
1197 ['XX'.$dum++,$self -> {'idcolumn'}-1];
1201 unshift( @
{$self -> {'secondary_columns'}}, \
@tmp );
1203 if ( $j == $cont_column ) {
1204 push( @tmp, ['CONT', undef] );
1207 if ( $#date_columns >= 0 ) {
1208 foreach my $col ( @date_columns ) {
1209 # This is a date column which may have to be dropped
1210 # and thus will not appear as a secondary
1211 # column. Nothing should be pushed. The indexes in
1212 # model::problem::pk::_format_record will be ok.
1213 $isdate = 1 if ( $col == $j ) ;
1217 push( @tmp, ['XX'.$dum++,$self -> {'idcolumn'}-1] );
1219 if ( $i <= $#secondary ) {
1220 push( @tmp, $secondary[$i] );
1223 push( @tmp, ['XX'.$dum++,$self -> {'idcolumn'}-1] );
1235 # {{{ have_missing_data
1236 start have_missing_data
1238 # Either I<column> or I<column_head> must be specified.
1240 # This method looks through the data column with index I<column> or
1241 # (optional) header name I<column_head> and returns O if no missing
1242 # data indicator was found or 1 otherwise.
1244 $self -> synchronize
;
1245 my $first_id = $self -> {'individuals'}[0];
1246 debug
-> die( message
=> "No individuals defined in data object based on ".
1247 $self -> full_name
) unless ( defined $first_id );
1248 my @data_row = split( /,/ , $first_id -> subject_data
-> [0] );
1249 unless ( defined $column && defined( $data_row[$column-1] ) ) {
1250 unless(defined($column_head) && defined($self -> {'column_head_indices'}{$column_head})){
1251 die "Error in data -> have_missing_data: unknown column: \"$column_head\" or invalid column number: \"$column\"\n";
1253 $column = $self -> {'column_head_indices'}{$column_head};
1256 $self -> flush
if ( $self -> {'target'} eq 'disk' );
1258 # In case anyone wonders, the ternary statment ( bool ? true :
1259 # false ) below will possibly make a minuscle memory
1260 # optimization. But hey, why not :)
1262 $return_value = defined $self -> {'have_missing_data'} ?
$self -> {'have_missing_data'} -> {$column} : 0;
1264 end have_missing_data
1265 # }}} have_missing_data
1270 #$self -> synchronize;
1271 push( @
{$self -> {'individuals'}}, @
{$mergeobj -> individuals
} );
1280 # Either column or column_head must be specified. Column_head must be a string that
1281 # identifies a column in the (optional ) data file header.
1283 # The if-statement below used to be a cache of allready calculated
1284 # means. But since individuals can be accessed in so many ways, we
1285 # don't know when this cache should be updated. Its easier to
1286 # recalculate the max. Maybe we can include this optimization in the
1287 # future, if it turns out to be a bottleneck
1288 # my $tmp_column = $self -> {'column_head_indices'}{$column_head};
1289 # if ( defined $self -> {'max'}[$tmp_column] ) {
1290 # $return_value = $self -> {'max'}[$tmp_column] ;
1292 $self -> synchronize
;
1293 my $first_id = $self -> {'individuals'}[0];
1294 debug
-> die( message
=> "data -> max: No individuals defined in data object based on " .
1295 $self -> full_name
) unless defined $first_id;
1297 my @data_row = split( /,/ , $first_id -> subject_data
->[0] );
1299 unless ( defined $column && defined( $data_row[$column-1] ) ) {
1300 unless (defined($column_head) && defined($self -> {'column_head_indices'}{$column_head})) {
1301 die "Error in data -> max: unknown column: \"$column_head\" or invalid column number: \"$column\"\n";
1303 $column = $self -> {'column_head_indices'}{$column_head};
1306 foreach my $individual ( @
{$self -> {'individuals'}} ) {
1307 my $ifactors = $individual -> factors
( 'column' => $column );
1308 foreach ( keys %{$ifactors} ) {
1309 next if ( $_ == $self -> {'missing_data_token'} );
1310 if ( defined ($return_value) ) {
1311 $return_value = $_ > $return_value ?
$_ : $return_value;
1318 # $self -> {'max'}[$column] = $return_value;
1319 $self -> flush
if ( $self -> {'target'} eq 'disk' );
1331 my $tmp_column = $self -> {'column_head_indices'}{$column_head};
1333 # The if-statement below used to be a cache of allready calculated
1334 # means. But since individuals can be accessed in so many ways, we
1335 # don't know when this cache should be updated. Its easier to
1336 # recalculate the min. Maybe we can include this optimization in the
1337 # future, if it turns out to be a bottleneck
1338 # if ( defined $self -> {'min'}[$tmp_column] ) {
1339 # $return_value = $self -> {'min'}[$tmp_column] ;
1341 $self -> synchronize
;
1342 my $first_id = $self -> {'individuals'}[0];
1343 die "data -> min: No individuals defined in data object based on ",
1344 $self -> full_name
,"\n" unless defined $first_id;
1346 my @data_row = split( /,/ , $first_id -> subject_data
->[0] );
1348 unless ( defined $column && defined( $data_row[$column-1] ) ) {
1349 unless (defined($column_head) && defined($self -> {'column_head_indices'}{$column_head})) {
1350 die "Error in data -> min: unknown column: \"$column_head\" or invalid column number: \"$column\"\n";
1352 $column = $self -> {'column_head_indices'}{$column_head};
1355 foreach my $individual ( @
{$self -> {'individuals'}} ) {
1356 my $ifactors = $individual -> factors
( 'column' => $column );
1357 foreach ( keys %{$ifactors} ) {
1358 next if ( $_ == $self -> {'missing_data_token'} );
1359 if ( defined ($return_value) ) {
1360 $return_value = $_ < $return_value ?
$_ : $return_value;
1366 # $self -> {'min'}[$column] = $return_value;
1367 $self -> flush
if ( $self -> {'target'} eq 'disk' );
1379 $self -> synchronize
;
1380 my $first_id = $self -> {'individuals'}[0];
1381 die "data -> median: No individuals defined in data object based on ",
1382 $self -> full_name
,"\n" unless defined $first_id;
1384 my @data_row = split( /,/ , $first_id -> subject_data
->[0] );
1386 unless ( defined $column && defined( $data_row[$column-1] ) ) {
1387 unless(defined($column_head) && defined($self -> {'column_head_indices'}{$column_head})){
1388 die "Error in data -> median: unknown column: \"$column_head\" or invalid column number: \"$column\"\n";
1390 $column = $self -> {'column_head_indices'}{$column_head};
1394 if( defined $self -> {'median'}[$column] ){
1395 return $self -> {'median'}[$column];
1400 foreach my $individual ( @
{$self -> {'individuals'}} ) {
1401 if( $unique_in_individual ){
1402 my $ifactors = $individual -> factors
( 'column' => $column );
1404 foreach ( keys %{$ifactors} ) {
1405 next if ( $_ == $self -> {'missing_data_token'} );
1406 push( @median_array, $_ );
1409 my $ifactors = $individual -> subject_data
;
1411 for(my $i=0; $i<=$#{$ifactors}; $i++ ) {
1412 my @data_row = split( /,/ , $ifactors -> [$i] );
1413 next if ( $data_row[$column-1] == $self -> {'missing_data_token'} );
1414 push(@median_array, $data_row[$column-1]);
1418 @median_array = sort {$a <=> $b} @median_array ;
1420 if( @median_array % 2 ){
1421 $return_value = $median_array[$#median_array / 2];
1423 $return_value = ( $median_array[@median_array / 2] +
1424 $median_array[(@median_array - 2) / 2] ) / 2;
1427 $self -> {'median'}[$column] = $return_value;
1437 # Returns mean value of a column
1438 # If a individual contains more then 1 value (i.e. if an
1439 # individual has different values in different samples a mean
1440 # value of all individuals if calculate first, then the mean
1441 # value of the column If hi_cutoff is defined the mean function
1442 # will cut all value below the cutoff, and set their value to
1443 # 0. It's used to calculate the HI-mean/LOW-mean of a column for
1444 # e.g. Hockey-stick covariates If both hi_cutoff and low_cutoff
1445 # are defined only the hi_cutoff will be used. See L</max>.
1446 my $tmp_column = $self -> {'column_head_indices'}{$column_head};
1447 $self -> synchronize
;
1448 my $first_id = $self -> {'individuals'}[0];
1449 die "data -> mean: No individuals defined in data object based on ",
1450 $self -> full_name
,"\n" unless defined $first_id;
1452 my @data_row = split( /,/, $first_id -> subject_data
->[0] );
1454 unless ( defined $column && defined( $data_row[$column-1] ) ) {
1455 unless (defined($column_head) && defined($self -> {'column_head_indices'}{$column_head})) {
1456 die "Error in data -> mean: unknown column: \"$column_head\" or invalid column number: \"$column\"\n";
1458 $column = $self -> {'column_head_indices'}{$column_head};
1462 ## Here the calculation starts
1463 my $num_individuals = 0;
1466 my $all_data_rows=0;
1467 foreach my $individual ( @
{$self ->{'individuals'}} ) {
1469 my $ifactors = $individual -> subject_data
;
1470 my $individual_sum = 0;
1472 for(my $i=0; $i<=$#{$ifactors}; $i++ ) {
1474 # data is stored in strings. We need to split them into an
1477 my @data_row = split( /,/, $ifactors -> [$i] );
1478 if ( $data_row[$column-1] == $self -> {'missing_data_token'} ) {
1479 # print "Skipping row with missing data\n";
1483 if( defined $subset_column and not eval ( $data_row[$subset_column-1].$subset_syntax ) ) {
1484 # print "Skipping row outside subset: syntax: ".($subset_column-1)." $subset_syntax\n";
1488 if (defined $hi_cutoff) {
1489 if ($data_row[$column-1]>$hi_cutoff) {
1490 $individual_sum += $data_row[$column-1]-$hi_cutoff;
1494 if (defined $low_cutoff) {
1495 if ($data_row[$column-1]<$low_cutoff) {
1496 $individual_sum += $low_cutoff - $data_row[$column-1];
1500 $individual_sum += $data_row[$column-1];
1505 if( $global_mean ) {
1506 $sum += $individual_sum;
1507 $num_individuals += $data_rows;
1509 if( $data_rows != 0 ) {
1510 $sum += $individual_sum/$data_rows;
1512 $num_individuals ++;
1514 $all_data_rows += $data_rows;
1516 if( $num_individuals != 0 ) {
1517 $return_value = $sum / $num_individuals;
1519 # print "DR: $all_data_rows\n";
1520 # print "\nNIM: $num_individuals $return_value\n";
1531 # This sub returns standard deviation for a specific column
1532 # If there are more than one sample/individual the value used for that specific
1533 # individual is the mean value of its samples.
1534 # The cut-offs are for hockey stick variables. I.e. If one individual value is
1535 # lower than the hi-cutoff the individual value will be zero.
1536 # HI_cutoff is used to calculate the HI-mean of a column.
1537 # If cut_off is undef it won't be used
1539 my $tmp_column = $self -> {'column_head_indices'}{$column_head};
1540 $self -> synchronize
;
1541 my $first_id = $self -> {'individuals'}[0];
1542 debug
-> die( message
=> "No individuals defined in data object based on ".
1543 $self -> full_name
) unless defined $first_id;
1545 my @data_row = split( /,/ , $first_id -> subject_data
->[0] );
1547 unless ( defined $column && defined( $data_row[$column-1] ) ) {
1548 unless (defined($column_head) && defined($self -> {'column_head_indices'}{$column_head})) {
1549 debug
-> die( message
=> "Unknown column: \"$column_head\" or "
1550 ."invalid column number: \"$column\"" );
1552 $column = $self -> {'column_head_indices'}{$column_head};
1556 ## Here the calculation starts
1557 my $num_individuals = 0;
1560 if (defined $hi_cutoff) {
1561 $mean = $self->mean(column
=> $column,
1562 hi_cutoff
=> $hi_cutoff,
1563 global_mean
=> $global_sd );
1564 } elsif (defined $low_cutoff) {
1565 $mean = $self->mean(column
=> $column,
1566 low_cutoff
=> $low_cutoff,
1567 global_mean
=> $global_sd );
1569 $mean = $self->mean( column
=> $column,
1570 subset_column
=> $subset_column,
1571 subset_syntax
=> $subset_syntax,
1572 global_mean
=> $global_sd );
1575 foreach my $individual ( @
{$self -> {'individuals'}} ) {
1576 my $ifactors = $individual -> subject_data
;
1577 my $individual_sum = 0;
1579 for(my $i=0; $i<=$#{$ifactors}; $i++ ) {
1581 # data is stored in strings. We need to split them into an
1584 my @data_row = split( /,/, $ifactors -> [$i] );
1586 if ( $data_row[$column-1] == $self -> {'missing_data_token'} ) {
1587 # print "Skipping row with missing data\n";
1591 if( defined $subset_column and not eval ( $data_row[$subset_column-1].$subset_syntax ) ) {
1592 # print "Skipping row outside subset: syntax: ".($subset_column-1)." $subset_syntax\n";
1596 if (defined $hi_cutoff) {
1597 if ($data_row[$column-1]>$hi_cutoff) {
1599 $individual_sum += ($data_row[$column-1] - $hi_cutoff - $mean) ** 2;
1601 $individual_sum += $data_row[$column-1]-$hi_cutoff;
1605 if (defined $low_cutoff) {
1606 if ($data_row[$column-1]<$low_cutoff) {
1608 $individual_sum += ($low_cutoff - $data_row[$column-1] - $mean) ** 2;
1610 $individual_sum += $low_cutoff - $data_row[$column-1];
1615 $individual_sum += ($data_row[$column-1] - $mean) ** 2;
1617 $individual_sum += $data_row[$column-1];
1624 $sum += $individual_sum;
1625 $num_individuals += $data_rows;
1627 if( $data_rows != 0 ) {
1628 $sum += ($individual_sum/$data_rows - $mean) ** 2;
1633 if( $num_individuals < 2 ) {
1636 if( $num_individuals != 0 ) {
1637 $return_value = (1/($num_individuals-1)*$sum) ** 0.5;
1650 my $tmp_column = $self -> {'column_head_indices'}{$column_head};
1651 if ( defined $self -> {'range'}[$tmp_column] ) {
1652 $return_value = $self -> {'range'}[$tmp_column];
1654 my $old_target = $self -> {'target'};
1655 $self -> {'target'} = 'mem';
1656 $self -> synchronize
;
1657 $return_value = $self -> max
( column
=> $column,
1658 column_head
=> $column_head ) -
1659 $self -> min
( column
=> $column,
1660 column_head
=> $column_head );
1661 $self -> {'range'}[$column] = $return_value;
1662 if ( $old_target eq 'disk' ) {
1663 $self -> flush
if ( $self -> {'target'} eq 'disk' );
1664 $self -> {'target'} = 'disk';
1674 # Recalculates a column based on expression. Also, see L</max>.
1675 $self -> synchronize
;
1677 # Check if $column(-index) is defined and valid, else try to find index using column_head
1678 my $first_id = $self -> {'individuals'}[0];
1679 die "data -> recalc_column: No individuals defined in data object based on ",
1680 $self -> full_name
,"\n" unless defined $first_id;
1682 my @data_row = split( /,/ , $first_id -> subject_data
->[0] );
1684 unless ( defined $column && defined( $data_row[$column-1] ) ) {
1685 if(defined($column_head) && defined($self -> {'column_head_indices'}{$column_head})){
1686 die "Error in data -> recalc_column: unknown column: \"$column_head\" or column number: \"$column\"\n";
1688 $column = $self -> {'column_head_indices'}{$column_head};
1692 for my $individual ( @
{$self -> {'individuals'}} ) {
1693 $individual -> recalc_column
( column
=> $column,
1694 expression
=> $expression );
1700 # {{{ renumber_ascending
1702 start renumber_ascending
1704 # Renumbers the individuals (changes the subject identifiers) so that
1705 # all have unique integer numbers starting with start_at and
1706 # ascending. The primary use of this
1707 # method is not to order the individuals after their identifiers but to
1708 # ensure that all individuals have unique identifiers.
1710 $self -> synchronize
;
1711 foreach my $individual ( @
{$self -> {'individuals'}} ) {
1712 $individual -> idnumber
( $start_at++ );
1714 $self -> {'synced'} = 0;
1716 end renumber_ascending
1718 # }}} renumber_ascending
1720 # {{{ renumber_descending
1722 start renumber_descending
1724 # See L</renumber_ascending>.
1725 $self -> synchronize
;
1726 foreach my $individual ( @
{$self -> {'individuals'}} ) {
1727 $individual -> idnumber
( $start_at-- );
1729 $self -> {'synced'} = 0;
1731 end renumber_descending
1733 # }}} renumber_descending
1735 # {{{ single_valued_data
1737 start single_valued_data
1741 # ($single_value_data_set, $remainder, $column_indexes) =
1742 # $data_object -> single_valued_data( subset_name => 'subset.dta',
1743 # remainder_name => 'remainder.dta',
1745 # do_not_test_columns => [1..18,24,26];
1747 # my $single_value_column_indexes = $column_indexes -> [0];
1748 # my $all_other_column_indexes = $column_indexes -> [1];
1750 # Analyses the content of each column, based on the
1751 # ID column, and returns two new data objects: One
1752 # that contains all columns that is has only one value per
1753 # individual and one that contains the
1754 # remainding data. This is useful for creating compact 'extra'
1755 # data sets that can be read in via user-defined sub-routines
1756 # when the number of columns needed exceeds the maximum that
1757 # NONMEM allows (e.g. 20 in NONMEM version V).
1759 # The I<do_not_test_columns> argument specifies on which columns
1760 # to skip the single value test
1762 my @multi_value_flags;
1763 my @individuals = @
{$self -> {'individuals'}};
1764 # Initiate the flags:
1765 if ( defined $individuals[0] ) {
1766 my @data = @
{$individuals[0] -> {'subject_data'}};
1767 my @data_row = split( /,/ , $data[0] );
1768 for ( my $i = 0; $i < scalar @data_row; $i++ ) {
1770 foreach my $dntc ( @do_not_test_columns ) {
1771 $dnt_flag = 1 if ( $i == $dntc - 1 );
1773 $multi_value_flags[$i] = $dnt_flag;
1776 die "data -> single_valued_data: No data in ID number 1\n";
1779 for ( my $id = 0; $id <= $#individuals; $id++ ) {
1780 my @data = @
{$individuals[$id] -> {'subject_data'}};
1781 my @data_row = split( /,/, $data[0] );
1782 for ( my $j = 0; $j < scalar @data_row; $j++ ) {
1784 for ( my $i = 0; $i <= $#data; $i++ ) {
1785 my @data_row = split( /,/ , $data[$i] );
1786 $col_unique{$data_row[$j]}++;
1788 my $factors = scalar keys %col_unique;
1789 $multi_value_flags[$j]++ if ( $factors > 1 );
1792 for ( my $i = 0; $i <= $#multi_value_flags; $i++ ) {
1793 if ( $multi_value_flags[$i] ) {
1794 push ( @
{$column_indexes[1]}, $i + 1);
1796 push ( @
{$column_indexes[0]}, $i + 1);
1799 ( $single_value_data_set, $remainder ) =
1800 $self -> subset_vertically
( column_indexes
=> $column_indexes[0],
1801 subset_name
=> $subset_name,
1802 return_remainder
=> 1,
1803 remainder_name
=> $remainder_name,
1805 keep_first_row_only
=> 1);
1807 end single_valued_data
1811 # {{{ subset_vertically
1813 start subset_vertically
1817 # $subset = $data_object -> subset_vertically ( column_indexes => [1,2,6],
1818 # subset_name => 'subset.dta' );
1820 # This basic usage returns a new data object containing
1821 # columns 1,2 and 6 from the original data plus the
1822 # idcolumn. The new data object will be associated with the
1823 # file 'subset.dta'.
1825 # You get the remaining data, i.e. the original data minus
1826 # the created subset by specifying
1828 # ( $subset, $remainder ) =
1829 # $data_object -> subset_vertically ( column_indexes => [1,2,6],
1830 # subset_name => 'subset.dta',
1831 # return_remainder => 1,
1832 # remainder_name => 'remainder.dta' );
1834 # If you would like to flush the created data sets to disk and
1835 # save memory, set the I<target> argument to 'disk'. The
1836 # default value 'mem' will keep the whole data object in RAM.
1838 # The I<keep_first_row_only> argument can be used to reduce
1839 # the size of the subset data obejct by excluding all but the
1840 # first row of data from each individual.
1842 my @individuals = @
{$self -> {'individuals'}};
1843 # Create remainder index array if necessary
1844 my @remainder_indexes;
1845 if ( defined $individuals[0] ) {
1846 my @data = @
{$individuals[0] -> {'subject_data'}};
1847 my $idcolumn = $individuals[0] -> {'idcolumn'};
1848 # print "IC: $idcolumn\n";
1850 foreach my $use_index ( @column_indexes ) {
1851 $id_flag = 1 if ( $use_index == $idcolumn );
1853 if ( $return_remainder ) {
1854 # @remainder_indexes = ( $idcolumn );
1855 for ( my $i = 0; $i < scalar split(/,/,$data[0]); $i++ ) {
1857 foreach my $use_index ( @column_indexes ) {
1858 $rem_flag = 0 if ( $i == $use_index -1 );
1860 # $i == $idcolumn -1 );
1862 push( @remainder_indexes, $i + 1 ) if ( $rem_flag );
1864 unshift( @remainder_indexes, $idcolumn ) if ( $id_flag );
1866 unshift( @column_indexes, $idcolumn ) unless ( $id_flag );
1868 die "data -> single_valued_data: No data in ID number 1\n";
1873 for ( my $id = 0; $id <= $#individuals; $id++ ) {
1874 my $idnumber = $individuals[$id] -> idnumber
;
1875 my $idcolumn = $individuals[$id] -> idcolumn
;
1876 my @data = @
{$individuals[$id] -> {'subject_data'}};
1879 my $use_rows = $keep_first_row_only ?
0 : $#data;
1880 for ( my $i = 0; $i <= $use_rows; $i++ ) {
1882 my @data_row = split( /,/, $data[$i] );
1883 foreach my $use_index ( @column_indexes ) {
1884 push( @new_row, $data_row[$use_index-1] );
1886 # print "@new_row $#new_row\n";
1887 push( @new_data, join( ',', @new_row ) );
1889 for ( my $i = 0; $i <= $#data; $i++ ) {
1890 if ( $return_remainder ) {
1892 my @data_row = split( /,/, $data[$i] );
1893 foreach my $use_index ( @remainder_indexes ) {
1894 push( @new_row_2, $data_row[$use_index-1] );
1896 # print "@new_row_2 $#new_row_2\n";
1897 push( @new_data_2, join( ',' , @new_row_2 ) );
1900 my $new_id = data
::individual
-> new
( idnumber
=> $idnumber,
1901 idcolumn
=> $idcolumn,
1902 subject_data
=> \
@new_data );
1903 push( @new_ids, $new_id );
1904 if ( $return_remainder ) {
1906 $new_id_2 = data
::individual
-> new
( idnumber
=> $idnumber,
1907 idcolumn
=> $idcolumn,
1908 subject_data
=> \
@new_data_2 );
1909 push( @new_ids_2, $new_id_2 );
1912 my @header = @
{$self -> {'header'}};
1914 foreach my $use_index ( @column_indexes ) {
1915 push( @new_header, @header[$use_index-1] );
1918 if( defined $self -> {'comment'} ){
1919 my @comment = @
{$self -> {'comment'}};
1920 $comment = \
@comment;
1922 $subset = data
-> new
( filename
=> $subset_name,
1923 directory
=> $self -> {'directory'},
1924 ignoresign
=> $self -> {'ignoresign'},
1925 header
=> \
@new_header,
1926 comment
=> $comment,
1927 individuals
=> \
@new_ids,
1929 ignore_missing_files
=> 1 );
1930 if ( $return_remainder ) {
1932 foreach my $use_index ( @remainder_indexes ) {
1933 push( @new_header_2, @header[$use_index-1] );
1935 $remainder = data
-> new
( filename
=> $remainder_name,
1936 directory
=> $self -> {'directory'},
1937 ignoresign
=> $self -> {'ignoresign'},
1938 header
=> \
@new_header_2,
1939 comment
=> $comment,
1940 individuals
=> \
@new_ids_2,
1942 ignore_missing_files
=> 1 );
1945 end subset_vertically
1953 # if ( defined $expression and defined $bins ) {
1954 # die "data -> subset: expression and bins may not both be specified\n";
1956 # if ( not ( defined $expression or defined $bins ) ) {
1957 # die "data -> subset: expression or bins must be specified\n";
1959 $self -> synchronize
;
1960 my @header = @
{$self -> {'header'}};
1961 my @comment = defined $self -> {'comment'} ? @
{$self -> {'comment'}} : ();
1965 my @ids = @
{$self -> {'individuals'}};
1966 if ( defined $stratify_on ) {
1967 my $work_data = $self -> copy
( filename
=> 'work_data.dta',
1969 my %strata = %{$work_data -> factors
( column
=> $stratify_on )};
1970 # $Data::Dumper::Maxdepth = 1;
1971 # print Dumper \%strata;
1973 while ( my ( $factor, $keys ) = each %strata ) {
1974 foreach my $key ( @
{$keys} ) {
1976 while ( defined $rnd_ids{$factor}{$rnd_num} ) {
1979 $rnd_ids{$factor}{$rnd_num} = $ids[$key];
1983 while ( my ( $factor, $rnd_nums ) = each %rnd_ids ) {
1984 my @sort_rnd_nums = sort { $a <=> $b } keys %{$rnd_nums};
1985 for ( my $i = 0; $i <= $#sort_rnd_nums; $i ) {
1986 for ( my $j = 0; $j < $bins; $j++ ) {
1988 push( @subset_ids, [$rnd_ids{$factor}{$sort_rnd_nums[$i]} -> copy
] );
1989 push( @incl_ids, [$rnd_ids{$factor}{$sort_rnd_nums[$i]} -> idnumber
] );
1991 push( @
{$subset_ids[$j]}, $rnd_ids{$factor}{$sort_rnd_nums[$i]} -> copy
);
1992 push( @
{$incl_ids[$j]}, $rnd_ids{$factor}{$sort_rnd_nums[$i]} -> idnumber
);
1995 last if $i > $#sort_rnd_nums;
2000 for ( my $j = 0; $j < $bins; $j++ ) {
2001 my $sdata = data
-> new
( header
=> \
@header,
2002 comment
=> \
@comment,
2003 ignoresign
=> $self -> {'ignoresign'},
2004 individuals
=> $subset_ids[$j],
2005 ignore_missing_files
=> 1,
2007 idcolumn
=> $self -> {'idcolumn'},
2008 filename
=> "subset_$j.dta" );
2010 push( @subsets, $sdata );
2013 for ( my $i = 0; $i <= $#ids; $i++ ) {
2015 while ( defined $rnd_ids{$rnd_num} ) {
2018 $rnd_ids{$rnd_num} = $ids[$i];
2020 my @keys = sort { $a <=> $b } keys %rnd_ids;
2022 for ( my $i = 0; $i <= $#keys; $i ) {
2023 for ( my $j = 0; $j < $bins; $j++ ) {
2025 push( @subset_ids, [$rnd_ids{$keys[$i]} -> copy
] );
2026 push( @incl_ids, [$rnd_ids{$keys[$i]} -> idnumber
] );
2028 push( @
{$subset_ids[$j]}, $rnd_ids{$keys[$i]} -> copy
);
2029 push( @
{$incl_ids[$j]}, $rnd_ids{$keys[$i]} -> idnumber
);
2032 last if $i > $#keys;
2036 for ( my $j = 0; $j < $bins; $j++ ) {
2037 my $sdata = data
-> new
( header
=> \
@header,
2038 comment
=> \
@comment,
2039 ignoresign
=> $self -> {'ignoresign'},
2040 individuals
=> $subset_ids[$j],
2041 ignore_missing_files
=> 1,
2043 idcolumn
=> $self -> {'idcolumn'},
2044 filename
=> "subset_$j.dta" );
2046 push( @subsets, $sdata );
2058 $self -> synchronize
;
2059 my @header = @
{$self -> {'header'}};
2060 my @comment = defined $self -> {'comment'} ? @
{$self -> {'comment'}} : ();
2061 my @subset_inds = ();
2063 foreach my $individual ( @
{$self -> {'individuals'}} ) {
2064 if ( $individual -> evaluate_expression
( column
=> $based_on,
2065 expression
=> $expression ) ) {
2066 push( @subset_inds, $individual -> copy
);
2067 push( @incl_individuals, $individual -> idnumber
);
2068 push( @included_keys, $key );
2072 $subset = data
-> new
( header
=> \
@header,
2073 comment
=> \
@comment,
2074 ignoresign
=> $self -> {'ignoresign'},
2075 individuals
=> \
@subset_inds,
2076 idcolumn
=> $self -> {'idcolumn'},
2077 filename
=> "subset.dta" );
2087 if ( $parm eq 'disk' and $self -> {'target'} eq 'mem' ) {
2088 $self -> {'target'} = 'disk';
2090 } elsif ( $parm eq 'mem' and $self -> {'target'} eq 'disk' ) {
2091 $self -> {'target'} = 'mem';
2092 $self -> synchronize
;
2103 die "ERROR: data -> _write: No filename set in data object.\n"
2104 if( $filename eq '' );
2106 # $Data::Dumper::Maxdepth = 2;
2107 # die Dumper $self -> {'individuals'};
2109 if( not defined $self -> {'individuals'} ){
2111 # If we don't have any individuals and write to a new
2112 # filename, we must first read individuals from the old
2113 # file. A call to synchronize will do that. There is no risk
2114 # of a infinite loop here since synchronize allways writes to
2117 unless( $filename eq $self -> full_name
){
2118 $self -> synchronize
;
2122 open(FILE
,">$filename") ||
2123 die "Could not create $filename\n";
2124 my $data_ref = $self -> format_data
;
2125 my @data = @
{$data_ref};
2131 # if ( $PsN::config -> {'_'} -> {'use_database'} and
2132 # $self -> {'use_data_table'} ) {
2133 # # Backslashes messes up the sql syntax
2134 # my $file_str = $self->{'filename'};
2135 # my $dir_str = $self->{'directory'};
2136 # $file_str =~ s/\\/\//g;
2137 # $dir_str =~ s/\\/\//g;
2140 # my $md5sum = md5_hex(OSspecific::slurp_file($self-> full_name ));
2141 # my ( $date_str, $time_str );
2142 # if ( $Config{osname} eq 'MSWin32' ) {
2143 # $date_str = `date /T`;
2144 # $time_str = ' '.`time /T`;
2147 # $date_str = `date`;
2151 # my $date_time = $date_str.$time_str;
2152 # my $dbh = DBI -> connect("DBI:mysql:host=".$PsN::config -> {'_'} -> {'database_server'}.
2153 # ";databse=".$PsN::config -> {'_'} -> {'project'},
2154 # $PsN::config -> {'_'} -> {'user'},
2155 # $PsN::config -> {'_'} -> {'password'},
2157 # 'RaiseError' => 1});
2159 # if ( defined $self -> {'data_id'} ) {
2160 # $sth = $dbh -> prepare( "UPDATE ".$PsN::config -> {'_'} -> {'project'}.
2162 # "SET filename='$file_str',date='$date_time',".
2163 # "directory='$dir_str',md5sum='$md5sum' ".
2164 # "WHERE data_id='".$self -> {'data_id'}."'" );
2165 # $sth -> execute or debug -> die( message => $sth->errstr ) ;
2167 # $sth = $dbh -> prepare("INSERT INTO ".$PsN::config -> {'_'} -> {'project'}.
2168 # ".data (filename,date,directory,md5sum) ".
2169 # "VALUES ('$file_str', '$date_time', '$dir_str','".
2172 # $self -> {'data_id'} = $sth->{'mysql_insertid'};
2175 # $dbh -> disconnect;
2185 # synchronizes the object with the file on disk and empties
2186 # most of the objects attributes to save memory.
2187 if( defined $self -> {'individuals'} and
2188 ( !$self -> {'synced'} or $force ) ) {
2191 # $self -> {'header'} = undef;
2192 $self -> {'comment'} = undef;
2193 $self -> {'individuals'} = undef;
2194 $self -> {'synced'} = 0;
2195 $self -> {'column_head_indices'} = undef;
2196 $self -> {'have_missing_data'} = undef;
2205 # synchronizes the object with the file on disk
2206 unless( $self -> {'synced'} ){
2207 if( defined $self -> {'individuals'} and
2208 scalar @
{$self -> {'individuals'}} > 0 ){
2209 # We should not read new data from file if we
2210 # have an individuals defined?
2211 # Perhaps there should be an attribute
2212 # 'from_file' that overrides this and reads in
2213 # the data from the file specified in filename
2214 # and overwrites whatever the object already
2216 # if( -e $self -> {'filename'} ){
2217 # $self -> _read_header;
2218 # $self -> _read_individuals;
2222 if( -e
$self -> full_name
){
2223 unless( defined $self -> {'header'} and scalar @
{$self -> {'header'}} > 0 ){
2224 $self -> _read_header
;
2226 $self -> _read_individuals
;
2228 debug
-> die( message
=> "Fatal error: datafile: " . $self -> full_name
. " does not exist." );
2234 foreach my $head ( @
{$self -> {'header'}} ){
2235 $self -> {'column_head_indices'} -> {$head} = $i;
2238 $self -> {'synced'} = 1;
2244 # {{{ _fisher_yates_shuffle
2246 start _fisher_yates_shuffle
2248 my $arr_ref = $parm{'array'};
2249 debug
-> warn( level
=> 1,
2250 message
=> "Array of zero length received" )
2251 if ( scalar @
{$arr_ref} < 1 );
2253 for ($i = @
$arr_ref; --$i; ) {
2254 my $j = random_uniform_integer
(1,0,$i);
2255 # my $j = int rand ($i+1);
2256 # print "$j $j_new\n";
2257 @
$arr_ref[$i,$j] = @
$arr_ref[$j,$i];
2260 end _fisher_yates_shuffle
2262 # }}} _fisher_yates_shuffle
2268 my $filename = $self -> full_name
;
2269 my $ignoresign = $self -> ignoresign
;
2270 my ( @data, @new_record, $row, $tmp_row, @header, $hdrstring );
2272 open(DATAFILE
,"$filename") ||
2273 die "Could not open $filename for reading";
2275 while (<DATAFILE
>) {
2278 # @new_record = split(/\,|\s+/,$_);
2279 if ( ! (/^\s*\d+|^\s*\./) ) {
2280 $data[$row] = $tmp_row;
2283 # We have reached the first data-row, return.
2284 $columns = scalar split(/\,\s*|\s+/);
2290 if ( defined $self -> {'cont_column'} and not $self -> {'table_file'} ) {
2291 my $data_len = $#data;
2292 for ( my $i = $data_len; $i >= 0; $i-- ) {
2293 my @arr = split(/\,\s*|\s+/,$data[$i]);
2294 if ( $arr[$self -> {'cont_column'}-1] eq 'CONT' ) {
2295 my $start = $i == $data_len ?
0 : 1;
2296 for ( my $j = $start; $j <= $#arr; $j++ ) {
2297 if ( $j != ($self -> {'cont_column'}-1) ) {
2298 push( @header, $arr[$j] );
2304 # the \Q and \E here are to escape wierd ignoresigns
2305 $header[0] =~ s/\Q$ignoresign\E//
2306 if ( defined $self->ignoresign );
2307 shift( @header ) if ( $header[0] eq "" );
2309 chomp( $hdrstring = pop(@data));
2310 @header = split(/\,\s*|\s+/,$hdrstring);
2311 # the \Q and \E here are to escape wierd ignoresigns
2312 $header[0] =~ s/\Q$ignoresign\E//
2313 if ( defined $self->ignoresign );
2314 shift( @header ) if ( $header[0] eq "" );
2315 if( $self -> {'table_file'} ) {
2317 for( my $i = 1; $i <= scalar @header; $i++ ) {
2318 if( $header[$i-1] eq 'CONT' ) {
2319 if ( defined $self -> {'cont_column'} and not $i == $self -> {'cont_column'} ) {
2320 debug
-> warn( level
=> 1,
2321 message
=> "The supplied columns for the CONT data item (".
2322 $self -> {'cont_column'}.") does not match the column where the CONT ".
2323 "header was found ($i), using $i" );
2325 $self -> {'cont_column'} = $i;
2327 push( @new_header, $header[$i-1] );
2330 @header = @new_header;
2331 for( my $i = 1; $i <= scalar @header; $i++ ) {
2332 if( $header[$i-1] eq 'ID' ) {
2333 if ( defined $self -> {'idcolumn'} and not $i == $self -> {'idcolumn'} ) {
2334 debug
-> warn( level
=> 1,
2335 message
=> "The supplied columns for the ID data item (".
2336 $self -> {'idcolumn'}.") does not match the column where the CONT ".
2337 "header was found ($i), using $i" );
2339 $self -> {'idcolumn'} = $i;
2345 # I'm not certain on how to deal with this conflict. I'm leaving it commented because I believe this code should not be here.
2347 #<<<<<<< data_subs.pm
2348 # $header[0] =~ s/$ignoresign//
2349 # if ( defined $self->ignoresign );
2350 # shift( @header ) if ( $header[0] eq "" );
2354 # It is ok with data sets without a header.
2355 # unless( scalar @header > 0 ){ debug -> die( message => 'Datafile ' . $self -> full_name . ' is empty.' ); }
2357 $self -> {'header'} = \
@header;
2358 $self -> {'comment'} = \
@data;
2359 # if ( $PsN::config -> {'_'} -> {'use_database'} and
2360 # $self -> {'use_data_table'} ) {
2361 # my $dbh = DBI -> connect("DBI:mysql:host=".$PsN::config -> {'_'} -> {'database_server'}.
2362 # ";databse=".$PsN::config -> {'_'} -> {'project'},
2363 # $PsN::config -> {'_'} -> {'user'},
2364 # $PsN::config -> {'_'} -> {'password'},
2365 # {'RaiseError' => 1});
2366 # if ( scalar @header < 1 ) {
2367 # for ( my $i = 1; $i <= $columns; $i++ ) {
2368 # push( @header, $i );
2371 # for ( my $i = 0; $i <= $#header; $i++ ) {
2372 # my $sth = $dbh -> prepare("INSERT INTO ".$PsN::config -> {'_'} -> {'project'}.
2374 # "(name,number,data_id) ".
2375 # "VALUES ('".$header[$i]."', '".($i+1).
2376 # "', '".$self -> {'data_id'}."' )");
2378 # push( @{$self -> {'data_column_ids'}}, $sth->{'mysql_insertid'} );
2381 # $dbh -> disconnect;
2388 # {{{ _read_individuals
2390 start _read_individuals
2392 my $idcol = $self -> idcolumn
;
2393 my $filename = $self -> full_name
;
2394 #debug -> warn( level => 1,
2395 # message => "Building array of individuals from file " . $self -> {'filename'} );
2396 open(DATAFILE
,"$filename") ||
2397 die "Could not open $filename for reading";
2398 my ( @new_row, $new_ID, $old_ID, @init_data );
2401 while (sysread DATAFILE
, $buffer, 4096) {
2402 $lines += ($buffer =~ tr/\n//);
2404 seek( DATAFILE
, 0,0 );
2407 my $status_bar = status_bar
-> new
( steps
=> $lines );
2409 ui
-> print( category
=> 'scm',
2410 message
=> "Reading data file: ".$self -> filename
);
2411 ui
-> print( category
=> 'scm',
2412 message
=> $status_bar -> print_step
(),
2415 my ( $sth, $dbh, $first_row_id, $first_value_id );
2417 # if ( $PsN::config -> {'_'} -> {'use_database'} and
2418 # $self -> {'use_data_table'} ) {
2419 # $dbh = DBI -> connect("DBI:mysql:host=".$PsN::config -> {'_'} -> {'database_server'}.
2420 # ";databse=".$PsN::config -> {'_'} -> {'project'},
2421 # $PsN::config -> {'_'} -> {'user'},
2422 # $PsN::config -> {'_'} -> {'password'},
2423 # {'RaiseError' => 1});
2424 # my $sth = $dbh -> prepare( "SELECT data_row_id FROM ".$PsN::config -> {'_'} -> {'project'}.
2426 # "WHERE data_id='".$self -> {'data_id'}."'" );
2427 # $sth -> execute or debug -> die( message => $sth->errstr ) ;
2428 # my $select_arr = $sth -> fetchall_arrayref;
2429 # if ( scalar @{$select_arr} > 0 ) {
2430 # for ( my $i = 0; $i < scalar @{$select_arr}; $i++ ) {
2431 # push( @{$self -> {'data_row_ids'}}, $select_arr->[$i][0] );
2433 # $sth = $dbh -> prepare( "SELECT data_value_id FROM ".$PsN::config -> {'_'} -> {'project'}.
2435 # "WHERE data_id='".$self -> {'data_id'}."'" );
2436 # $sth -> execute or debug -> die( message => $sth->errstr ) ;
2437 # my $select_val = $sth -> fetchall_arrayref;
2438 # for ( my $i = 0; $i < scalar @{$select_val}; $i++ ) {
2439 # push( @{$self -> {'data_value_ids'}}, $select_val->[$i][0] );
2442 # $dbh -> disconnect;
2444 # $dbh -> do( "LOCK TABLES ".$PsN::config -> {'_'} -> {'project'}.
2445 # ".data_row WRITE, ".$PsN::config -> {'_'} -> {'project'}.
2446 # ".data_value WRITE" );
2447 # $sth = $dbh -> prepare( "SELECT MAX(data_row_id) FROM ".$PsN::config -> {'_'} -> {'project'}.
2449 # $sth -> execute or debug -> die( message => $sth->errstr ) ;
2450 # my $select_arr = $sth -> fetchall_arrayref;
2451 # $first_row_id = defined $select_arr -> [0][0] ? $select_arr -> [0][0] : 0;
2452 # $sth = $dbh -> prepare( "SELECT MAX(data_value_id) FROM ".$PsN::config -> {'_'} -> {'project'}.
2454 # $sth -> execute or debug -> die( message => $sth->errstr ) ;
2455 # my $select_arr = $sth -> fetchall_arrayref;
2456 # $first_value_id = defined $select_arr -> [0][0] ? $select_arr -> [0][0] : 0;
2463 my $row_counter = 0;
2465 ROW
: while ( <DATAFILE
> ) {
2468 my @new_row = split(/\,\s*|\s+/);
2469 # This regexp check is not time consuming.
2470 if ( /^\s*\d+|^\s*\./ ) {
2471 if ( defined $self -> {'cont_column'} ) {
2472 if ( $new_row[$self -> {'cont_column'} - 1] == 1 ) {
2473 if ( not $self -> {'table_file'} ) { # Skip the CONT=1 rows if this is a table file
2474 for ( my $i = $#new_row; $i > 0; $i-- ) {
2475 if ( $i != ($self -> {'cont_column'} - 1) ) {
2476 unshift( @
{$full_row}, $new_row[$i] );
2482 for ( my $i = $#new_row; $i >= 0; $i-- ) {
2483 # if ( $i != ($self -> {'cont_column'} - 1) or $self -> {'table_file'} ) {
2484 if ( $i != ($self -> {'cont_column'} - 1) ) {
2485 unshift( @
{$full_row}, $new_row[$i] );
2490 @
{$full_row} = @new_row;
2492 $new_ID = $full_row -> [$idcol-1]; # index starts at 0
2493 $old_ID = $new_ID if ( not defined $old_ID );
2495 # Check if column miss data at some row (This adds about 30% of init time)
2496 my $mdt = $self -> {'missing_data_token'};
2497 for( my $i = 0; $i <= $#{$full_row}; $i++ ){
2498 $self -> {'have_missing_data'} -> {$i+1} = 1
2499 if( $full_row -> [$i] == $mdt ); # == is slower but safer than eq
2501 # if ( $PsN::config -> {'_'} -> {'use_database'} and
2502 # $self -> {'use_data_table'} and $insert ) {
2504 # $insert_rows = $insert_rows."," if ( defined $insert_rows );
2505 # $insert_rows = $insert_rows.
2506 # "('$row_counter', '".$self -> {'data_id'}."' )";
2507 # for ( my $j = 0; $j <= $#{$full_row}; $j++ ) {
2508 # $insert_values = $insert_values."," if ( defined $insert_values );
2509 # $insert_values = $insert_values.
2510 # "('".$full_row -> [$j]."', '".
2511 # ($first_row_id+$row_counter)."', '".
2512 # $self -> {'data_column_ids'}->[$j].
2513 # "', '".$self -> {'data_id'}."' )";
2517 if ( $new_ID != $old_ID ) {
2518 my @subject_data = @init_data;
2519 my $id = data
::individual
-> new
( idcolumn
=> $idcol,
2520 subject_data
=> \
@subject_data,
2521 data_id
=> $self -> {'data_id'} );
2522 push( @
{$self -> {'individuals'}}, $id );
2523 @init_data =(join( ",", @
{$full_row}));
2525 push( @init_data, join( ",", @
{$full_row}) );
2530 if ( $status_bar -> tick
() ) {
2531 ui
-> print( category
=> 'scm',
2532 message
=> $status_bar -> print_step
(),
2538 # if ( $PsN::config -> {'_'} -> {'use_database'} and
2539 # $self -> {'use_data_table'} and $insert ) {
2540 # $dbh -> do("INSERT INTO ".$PsN::config -> {'_'} -> {'project'}.
2542 # "(number,data_id) ".
2543 # "VALUES ".$insert_rows);
2544 # push( @{$self -> {'data_row_ids'}}, ($first_row_id..$first_row_id+$row_counter) );
2545 # $dbh -> do( "INSERT INTO ".$PsN::config -> {'_'} -> {'project'}.
2547 # "(value,data_row_id,data_column_id,data_id) ".
2548 # "VALUES ".$insert_values );
2549 # push( @{$self -> {'data_value_ids'}},
2550 # ($first_value_id..$first_value_id+($row_counter*
2551 # scalar @{$self->{'data_column_ids'}})));
2552 # $dbh -> do( "UNLOCK TABLES" );
2553 # $dbh -> disconnect;
2556 if ( $#init_data >= 0 ) {
2557 push( @
{$self -> {'individuals'}},
2558 data
::individual
-> new
( idcolumn
=> $idcol,
2559 subject_data
=> \
@init_data ) );
2561 ui
-> print( category
=> 'scm',
2562 message
=> " ... done" );
2564 # $self -> _write( filename => 'test.dta' );
2566 end _read_individuals
2568 # }}} _read_individuals