接下来的问题是我试图解决的更复杂问题的简化示例。我想保留代码的结构,特别是使用%hash
来存储每个患者的结果,但我不需要将数据文件读入内存(但我找不到一种方法来阅读我的csv数据文件从最后逐行。)
我的样本数据由患者发生的事件组成。可以将患者添加到研究中(事件= B)或者他可以死亡(事件= D)或退出研究(事件= F)。死亡和退出是每个患者的唯一两种可能结果。
对于每个事件,我都有发生日期(从给定时间点开始的小时数),每个患者的唯一ID号,事件和结果(每个患者的字段设置为0)。
我正在尝试编写一个代码,通过在新患者的每次添加旁边添加来更改输入文件,他的最终结果是什么(死亡或退出。)
为了做到这一点,我从最后读取文件,每当我遇到病人的死亡或退出时,我都会填写一个与患者ID匹配的哈希值。当我遇到一个事件告诉我已经将新患者添加到研究中时,我将他的ID与哈希中的ID匹配,并将“结果”的值从0更改为D或F.
我已经能够编写一个从底部读取文件的代码,然后使用更新的Outcome值创建一个新的修改文件。问题是,因为我从下到上读取输入文件并在读取后打印每一行,输出文件的顺序相反,我不知道如何更改它。另外,理想情况下我不想创建新文件,我只想修改输入文件。但是,我在每次尝试时都失败了。
示例数据:
Data,PatientNumber,Event,Outcome
25201027,562962838335407,B,0
25201028,562962838335408,B,0
25201100,562962838335407,D,0
25201128,562962838335408,F,0
我的代码:
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;
open (my $fh_input, "<", "mini_test2.csv")
or die "cannot open > mini_test2.csv: $!";
my @lines = <$fh_input>;
close $fh_input;
open (my $fh_output, ">>", "Revised_mini_test2.csv")
or die "cannot open > Revised_mini_test2.csv: $!";
my $length = scalar(@lines);
my %outcome;
my @input_variables;
for (my $i = 1; $i < @lines; $i++){
chomp($lines[$length-$i]);
@input_variables=split(/,/, $lines[$length - $i]);
if ($input_variables[2] eq "D" || $input_variables[2] eq "F"){
$outcome{$input_variables[1]} = $input_variables[2];
my $line = join(",", @input_variables);
print $fh_output $line . "\n";
}
elsif($input_variables[2] eq "B") {
$input_variables[3]=$outcome{$input_variables[1]};
my $line = join(",", @input_variables);
print $fh_output $line . "\n";
}
else{
# necessary since the actual data has many more possible "Events"
my $line = join(",", @input_variables);
print $fh_output $line . "\n";
}
}
close $fh_output;
编辑:所需的输出应为
Data,PatientNumber,Event,Outcome
25201027,562962838335407,B,D
25201028,562962838335408,B,F
25201100,562962838335407,D,0
25201128,562962838335408,F,0
此外,另一个复杂因素是患者出院后的唯一患者ID被重新使用。这意味着我无法进行第一次传递并为每位患者存储结果,而第二次传递则更新结果值。
编辑2:让我澄清一下,当我说每个患者都有一个“唯一ID”时,我的意思是在研究中不能同时有两个具有相同身份证的患者。但是,如果患者退出研究,他的身份证会重新使用。
答案 0 :(得分:0)
我刚刚阅读了您的其他信息,一旦他们退出研究,就会重复使用患者编号。为什么要设计一个我不知道的系统,但它就是
在不将文件读入数组的情况下编写简单的内容变得更加困难,这就是我在这里所做的事情
use strict;
use warnings;
use 5.010;
use autodie;
open my $fh, '<', 'mini_test2.csv';
my @data;
while ( <$fh> ) {
chomp;
push @data, [ split /,/ ];
}
my %outcome;
for ( my $i = $#data; $i > 0; --$i ) {
my ($patient_number, $event) = @{$data[$i]}[1,2];
if ( $event =~ /[DF]/ ) {
$outcome{$patient_number} = $event;
}
elsif ( $event =~ /[B]/ ) {
$data[$i][3] = delete $outcome{$patient_number} // 0;
}
}
print join(',', @$_), "\n" for @data;
<强>输出强>
Data,PatientNumber,Event,Outcome
25201027,562962838335407,B,D
25201028,562962838335408,B,F
25201100,562962838335407,D,0
25201128,562962838335408,F,0
有几种方法可以解决这个问题。我选择在文件中进行两次传递,首先在哈希中累积每个患者的结果,然后替换B
记录中的所有结果字段
use strict;
use warnings;
use 5.010;
use autodie;
use Fcntl ':seek';
my %outcome;
open my $fh, '<', 'mini_test2.csv';
<$fh>; # Drop header
while ( <$fh> ) {
chomp;
my @fields = split /,/;
my ($patient_number, $event) = @fields[1,2];
if ( $event =~ /[DF]/ ) {
$outcome{$patient_number} = $event;
}
}
seek $fh, 0, SEEK_SET; # Rewind
print scalar <$fh>; # Copy header
while ( <$fh> ) {
chomp;
my @fields = split /,/;
my ($patient_number, $event) = @fields[1,2];
if ( $event !~ /[DF]/ ) {
$fields[3] = $outcome{$patient_number} // 0;
}
print join(',', @fields), "\n";
}
<强>输出强>
Data,PatientNumber,Event,Outcome
25201027,562962838335407,B,D
25201028,562962838335408,B,F
25201100,562962838335407,D,0
25201128,562962838335408,F,0
答案 1 :(得分:-1)
我们可以做的不是在每个阶段打印出行,而是将其写回行数组。然后我们可以在最后打印出来。
for (my $i=$#lines; i>=0; i--)
{
chomp $lines[$i];
@input_variables = split /,/, $lines[$i];
if ($input_variables[2] eq "D" || $input_variables[2] eq "F")
{
$outcome{$input_variables[1]}=$input_variables[2];
}else
{
$input_variables[3]=$outcome{$input_variables[1]};
}
$line[$i] = join ",", @input_variables;
}
$, = "\n"; #Make list seperator for printing a newline.
print $fh_output @lines;
关于修改原始文件的第二个问题。可以使用模式“+&lt;”,“+&gt;”或“+&gt;&gt;”打开用于读取和写入的文件。 不要这样做!由于必须逐字符替换数据,因此容易出错。
“修改”现有文件的标准方法是重命名,从重命名的文件中读取,使用原始名称写入新文件,然后删除临时文件。
my $file_name = "mini_test2.csv";
my $tmp_file_name = $file_name . ".tmp";
rename $file_name, $tmp_file_name;
open (my $fh_input, "<", $tmp_file_name)
or die "cannot open > $tmp_file_name: $!";
open (my $fh_output, ">>", $file_name)
or die "cannot open > $file_name: $!";
#Your code to process the data.
close $fh_input;
close $fh_output;
#delete the temp file
unlink $tmp_file_name;
但是,在您的情况下,您立即将所有数据放入内存中。只是打开写那些clobbers现有文件
open (my $fh_output, ">", "mini_test2.csv")
or die "cannot open > mini_test2.csv: $!";