【问题标题】:Perl, generating new data (new hash) using two different hash tablesPerl,使用两个不同的哈希表生成新数据(新哈希)
【发布时间】:2012-04-24 13:57:08
【问题描述】:

我遇到了一个非常复杂的问题(从我作为新手的角度来看),我不知道如何解决它。我能想到工作流程,但不会想到脚本。

我的文件 A 如下所示:Teacher (tab) Student1(space)Student2(space)..

Fiona       Nicole Sherry 
James       Alan Nicole
Michelle    Crystal 
Racheal     Bobby Dan Nicole

当有两个同名时(例如,John1、John2),有时他们的名字旁边会出现数字。如果他们有两个以上的顾问,学生也可能重叠..

文件 B 是一个包含教师组的文件。它看起来很相似,但值是用逗号分隔的。

Fiona       Racheal,Jack
Michelle    Racheal
Racheal     Fiona,Michelle
Jack        Fiona

文件B的趋势是一个key有多个value,每个value都变成一个key,方便查找谁和谁分组。

我想要的输出是哪些学生可能会根据他们的老师/小组接受类似的教育。所以我希望脚本执行以下操作:

  1. 将文件 A 存储到哈希中并关闭
  2. 打开文件B,查看每个老师是否有学生(有的可能没有,实际名单很大..)。所以如果我带第一个老师,Fiona,它会在存储的文件中查找一个哈希表,看看是否有一个 Fiona。如果有(在本例中为 Nicole 和 Sherry),将它们分别作为新哈希表的新键弹出。

    while (<Group>) {
        chomp;
        $data=$_;
        $data=~/^(\S+)\s+(.*)$/;
        $TeacherA=$1;
        $group=$2; 
    
  3. 然后,看看与 Fiona (Racheal, Jack) 分组的教师组。一次带 1 人 (Racheal)

    if (defined??) {
        while ($list=~/(\w+)(.*)/) {
            $TeacherB=$1;
            $group=$2;
    
  4. 查看 Racheal 学生的文件 A。
  5. 将它们填充为步骤 2 中生成的学生密钥的值(以逗号分隔)。
  6. 打印学生-学生和教师-教师组。

    Nicole  Bobby,Dan,Nicole    Fiona   Racheal
    Sherry  Bobby,Dan,Nicole    Fiona   Racheal
    

    由于 Fiona 小组的下一位老师 Jack 没有学生,所以他不会出现在这个结果中。例如,如果他有大卫,结果将是:

    Nicole  Bobby,Dan,Nicole    Fiona   Racheal
    Sherry  Bobby,Dan,Nicole    Fiona   Racheal
    Nicole  David               Fiona   Jack
    Sherry  David               Fiona   Jack
    

很抱歉提出如此复杂而具体的问题。我希望其他正在做类似事情的人可以从答案中受益。 非常感谢您的帮助和回复。你是我唯一的帮助来源。

【问题讨论】:

  • 我对文件B的内容感到困惑。文件B中的所有名字都属于教师吗?
  • 是的,文件 B 属于教师。对不起大家,作为一个新手,我正在做的事情可能看起来很奇怪,我确实开始手动操作,但我拥有的文件相当大,而不是我在这里显示的简化快照,并且花费了我很多时间和制作我同时感到困惑。
  • 使用 perl 而不是手动工作是个好主意。 Perl 非常适合这种事情。

标签: perl hashtable information-extraction


【解决方案1】:

这是查看数据的一种相当奇怪的方式,但我想我已经按照您尝试的方式让它工作了。看看你为什么希望数据是这样的会很有趣。也许下次提供列标题。知道你为什么以某种方式做某事通常会让你更容易想出实现它的方法。

这就是我所做的。不要混淆,我将文件 A 和文件 B 中的值转换为标量,并更改了读取它们的部分。

my $file_a = qq~Fiona\tNicole Sherry
James\tAlan Nicole
Michelle\tCrystal
Racheal\tBobby Dan Nicole
~;

my $file_b = qq~Fiona\tRacheal,Jack
Michelle\tRacheal
Racheal\tFiona,Michelle
Jack\tFiona
~;

之后,继续阅读“文件”。

# 1: Store file A in a hash
my (%file_a);
foreach my $a (split /\n/, $file_a) {
  my @temp = split /\t/, $a;
  $file_a{$temp[0]} = $temp[1];
}

# 2: Go through file B
foreach my $b (split /\n/, $file_b) {
  my @line_b = split /\t/, $b;
  # Look in stored file A if the teacher is there
  if (exists $file_a{$line_b[0]}) {
    my (%new_hash_table, @teachers);
    # Put all the students of this teacher into a new hash
    $new_hash_table{$_} = '' foreach split / /, $file_a{$line_b[0]};

    # 3: Take one of the group of teachers who are grouped with the 
    # current teacher at a time
    foreach my $teacher (split /,/, $line_b[1]) {
      if (exists $file_a{$teacher}) {
        # 4: This teacher from the group has students listen in file A
        push @teachers, $teacher; # Store the teacher's name for print later
        foreach (keys %new_hash_table) {
          # 5: Fill the students as csv for the student keys from step 2
          $new_hash_table{$_} = join(',', split(/ /, $file_a{$teacher}));
        }
      }
    }
    foreach my $student (keys %new_hash_table) {
      # 6: Print...        
      print join("\t", 
        # Student-student relation
        $student, $new_hash_table{$student}, 
        # Teacher-teacher relation
        $line_b[0], @teachers);
      print "\n";
    }
  }
}

对我来说提供以下输出:

Sherry  Bobby,Dan,Nicole    Fiona   Racheal
Nicole  Bobby,Dan,Nicole    Fiona   Racheal
Crystal Bobby,Dan,Nicole    Michelle    Racheal
Bobby   Crystal Racheal Fiona   Michelle
Nicole  Crystal Racheal Fiona   Michelle
Dan Crystal Racheal Fiona   Michelle

这可能很奇怪,因为我没有所有的值。

无论如何,对此有几件事要说。

在您的示例代码中,您使用了像 $data=~/^(\S+)\s+(.*)$/; 这样的正则表达式来获取简单的两列列表的值。使用split operator 做到这一点要容易得多。

当您从具有&lt;FILEHANDLE&gt; 语法的文件中读取时,您可以将您希望行进入的标量放入while 循环的条件中,如下所示:

while (my $data = <GROUP>) {
      chomp $data

同样common 以全大写形式写入文件句柄名称。

我建议你看看“Learning Perl”。里面的哈希和数组的基本概念应该足以处理这样的任务。希望这会有所帮助。

【讨论】:

  • 不要使用 Typeglobs,而是使用词法文件句柄。见stackoverflow.com/questions/3276674/…stackoverflow.com/questions/1479741/…
  • @simbabque-感谢您解决我的问题。我知道这听起来很奇怪。我只是有一个关于将我的两个文件更改为标量的问题。如果这是一个幼稚的问题,请原谅我,但是如果我的文件大小比我给出的示例大得多,那么这样做仍然是最好的方法吗?如果没有,您能否提出其他建议?
  • 不,不会。事实上,我只是出于懒惰才这样做,所以我不必创建文件。但是,无论您从哪里获取数据,上述程序的工作方式都是一样的。当然,在一个富有成效的程序中,您会希望您的输入从程序中移出。您希望将其作为参数(在本例中为两个文件名)传入,因为否则每次收到新数据时都必须更改程序代码。永远记住,在 perl 中有不止一种方法可以做到这一点,但很少有最好的方法。 ;-)
  • @simbabque - 实际上我在开头写了以下几行:'open $teacher, '
  • @absolutenewbie:请使用code 代码语法。您需要实际读取文件,而不仅仅是打开它。你也改了吗?请参阅 perldoc 上的 open 并查看我上面的最后一个示例。工作目录(即同一目录)中的文件是否与脚本本身?
【解决方案2】:

我无法想象为什么你会想要这些冗余数据,而你只需查看文件 A 就可以很好地了解谁正在接受类似的教育......但这里有一种在 perl 中完成的方法一样。

$data = {};
# pull in students
open(IN, "students.txt");
while(my $line = <IN>) {
  chomp($line);
  my ($teacher, @students) = split(/\s+/,$line);
  $data->{$teacher}->{students} = \@students;
}
close IN;
# pull in teachers
open(IN, "teachers.txt");
while(my $line = <IN>) {
  chomp($line);
  my ($teacher, $supporters) = split(/\s+/,$line);
  my @supporters = split(/,/,$supporters);
  $data->{$teacher}->{supporters} = \@supporters;
}
close IN;
# make the output
foreach my $teacher (keys %{$data}){
  foreach my $teacher_student (@{$data->{$teacher}->{students}}) {
    foreach my $supporter (@{$data->{$teacher}->{supporters}}){
      my $num_supporter_students = @{$data->{$supporter}->{students}} + 0;
      if($num_supporter_students) {

        print "$teacher_student\t" . 
              join(",",@{$data->{$supporter}->{students}}) .
              "\t$teacher\t$supporter\n";
      }
    }
  }
}

当对问题中列出的数据运行时,它返回:

Crystal Bobby,Dan,Nicole    Michelle    Racheal
Nicole  Bobby,Dan,Nicole    Fiona   Racheal
Sherry  Bobby,Dan,Nicole    Fiona   Racheal
Bobby   Nicole,Sherry   Racheal Fiona
Bobby   Crystal Racheal Michelle
Dan Nicole,Sherry   Racheal Fiona
Dan Crystal Racheal Michelle
Nicole  Nicole,Sherry   Racheal Fiona
Nicole  Crystal Racheal Michelle

【讨论】:

  • 使用for 代替foreachopen(IN, "&lt;","filename.txt")
  • @zortacon-first,非常感谢您解决我的问题。我想我明白你是怎么做到的。不幸的是,它给了我一个错误消息:“在 match_student.pl line24 的数组取消引用中使用未初始化的值。”和“在match_student.pl line24处使用未初始化的值(+)。”它也没有给我我想要的完整列表。我认为这只是比较第一场比赛(Fiona-Racheal 对)。
  • 该错误意味着教师在从学生文本文件中读取学生时没有设置学生。它正在将链接取消引用到一个数组中,并且在那里找不到任何东西。这应该只是一个警告,而不是阻止它运行。
  • 是的,它适用于我提供的示例集,但不适用于我拥有的实际文件。我想知道是什么导致了差异..
猜你喜欢
  • 2018-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-06
  • 2016-10-27
  • 1970-01-01
  • 2014-10-01
相关资源
最近更新 更多