[펄] 해시를 이용하여 특정조건에 부합하는 것들의 합을 구하기

momo7의 이미지

안녕하세요. 아래와 같은 데이터가 있습니다. 포인트, 동일년도 기준으로 특정조건을 만족시키는 기온의 합산을 할려고 하는데,

자꾸 제대로 된 결과값을 보여주질 않습니다. 아래 코드보시고 힌트좀 주실수 있습니까?

** 해시만 가지고 두가지 조건을 동시에 만족시키는 것을 잘 표현하기 어려워 for문을 더 썼습니다.

/데이터/

포인트 년월일 기온
90 19910101 10
90 19910102 20
90 19910103 19
91 19910101 15
91 19910102 19
91 19910103 25
90 19920101 30
90 19920102 30
90 19920103 10
91 19920101 30
91 19920102 20
91 19920103 10

/기대하는 결과/
90 1991 3
91 1991 8
90 1992 24
91 1992 14

#!/usr/bin/env perl
 
use strict;
use warnings;
use 5.010;
 
my($i,$point,$year,@sum,%sum,@arr);
 
for ($i=1991; $i<=1992;$i++) {
 
open my $fh, '<', "aws.txt";
while (<$fh>) {
	@arr=split;
		$point = $arr[0]; 
		$year = substr $arr[1], 0, 4;
			if ($year = $i){
				if ($arr[2] >= 18.0){
			$sum{$point}{$year}{d} += ($arr[2] - 18);
			}
	}
}
 
foreach(sort {$a<=>$b} keys(%sum)) {
    say join("\t",$_, $i, $sum{$_}{$year}{d});
   }
}

chanik의 이미지

텍스트 파일로부터 바로 답을 뽑아낼 수도 있겠지만,
DB에 테이블로 읽어들여서 SQL 쿼리 하나로 해결할 수도 있습니다.

DB는 임베디드엔진인 SQLite를 쓰고, perl을 몰라서 python으로 해봤습니다.
python 코드만 perl 코드로 바꾸면 되는데 제 능력 밖이네요..

우선 aws_tbl이라는 테이블을 만들고 aws.txt를 읽어들여 테이블을 채웁니다.
aws_tbl은 SQLite DB 파일인 aws.db 안에 만드는데, aws.db 파일은 없으면 저절로 만들어집니다.

이렇게 테이블이 준비되면 SQL 쿼리 하나로 원하는 결과를 얻을 수 있습니다.
다른 조건으로 데이터를 가공하고 싶으면 쿼리만 적당히 바꾸면 됩니다.

#!/usr/bin/python
# -*- coding: utf-8 -*-
 
import os
import sqlite
 
def fill_aws_tbl(cur, txtfile):
    cur.execute("SELECT name FROM sqlite_master"
                " WHERE type = 'table' AND name = 'aws_tbl'")
    if not cur.rowcount:
        cur.execute("CREATE TABLE aws_tbl (point integer, ymd text, temp integer)")
        lines = open(txtfile, 'r').readlines()
        recs  = ( line.split() for line in lines[1:] )  # 첫줄은 버리고 둘째 줄부터 처리
        cur.executemany("INSERT INTO aws_tbl VALUES(%s, %s, %s)", recs)
 
def analyze_aws(dbfile, txtfile):
    conn = sqlite.connect(dbfile)
    cur = conn.cursor()
 
    # aws_tbl 테이블이 존재하지 않으면 테이블을 만들고 데이터를 읽어들임
    # 이미 테이블이 존재하면 그냥 리턴
    fill_aws_tbl(cur, txtfile)
    conn.commit()
 
    cur.execute( "SELECT point, substr(ymd,1,4) 'year', sum(temp-18) 'sum'"
                 "  FROM aws_tbl"
                 " WHERE temp >= 18"
                 " GROUP BY 1, 2"
                 " ORDER BY 2, 1" )
 
    for row in cur:
        print row
 
    cur.close()
 
if __name__ == "__main__":
    analyze_aws("aws.db", "aws.txt")

http://gentooboy.tistory.com/136 를 참고했고,
테스트는 CentOS-5.10의 python-2.4에서 했습니다.

$ ./aws.py
(90, '1991', 3)
(91, '1991', 8)
(90, '1992', 24)
(91, '1992', 14)
chanik의 이미지

인터넷 대충 뒤셔서 perl로 바꿔봤습니다. 처음해본 것이니 코드는 의심스러우나 어쨌든 동작은 되네요.
작성중이신 코드를 보완할 방법을 질문하신 마당에 제가 방향이 맞지 않는 댓글만 달았습니다만,
이렇게 DB와 SQL에 의지하는 방법도 있음을 보이고 싶었습니다.

#!/usr/bin/env perl
 
#
# CentOS 5.10, perl v5.8.8 에서는 rpmforge-extras repo에서 DBD 드라이버를 설치해야 했음
# $ sudo yum --enablerepo=rpmforge-extras install perl-DBD-SQLite
#
 
use strict;
use warnings;
use DBI;
 
sub fill_aws_tbl {
    my ($dbh, $txtfile) = @_;
    my $sth = $dbh->prepare( "SELECT name FROM sqlite_master"
                           . " WHERE type = 'table' AND name = 'aws_tbl'");
    $sth->execute();
    # fetch하기 전에는 항상 $sth->rows() == 0 이 나와서 판별이 안됨
    while (my @row = $sth->fetchrow_array) {}
    if ($sth->rows() == 0) {
        $dbh->do("CREATE TABLE aws_tbl (point integer, ymd text, temp integer)");
        $sth = $dbh->prepare("INSERT INTO aws_tbl VALUES(?, ?, ?)");
        open my $fh, '<', $txtfile;
        while (<$fh>) {
            next if $. == 1;   # 첫줄은 건너뜀
            my @arr = split;
            $sth->execute($arr[0], $arr[1], $arr[2]);
        }
    }
    $sth->finish();
}
 
sub analyze_aws {
    my ($dbfile, $txtfile) = @_;
    my $dbh = DBI->connect(
        "dbi:SQLite:dbname=$dbfile",
        "",
        "",
        { RaiseError => 1 },
    ) or die $DBI::errstr;
    #printf("AutoCommit == %d\n", $dbh->{AutoCommit});
 
    # aws_tbl 테이블이 존재하지 않으면 테이블을 만들고 데이터를 읽어들임
    # 이미 테이블이 존재하면 그냥 리턴
    fill_aws_tbl($dbh, $txtfile);
 
    my $sth = $dbh->prepare( "SELECT point, substr(ymd,1,4) 'year', sum(temp-18) 'sum'"
                           . "  FROM aws_tbl"
                           . " WHERE temp >= 18"
                           . " GROUP BY 1, 2"
                           . " ORDER BY 2, 1" );
    $sth->execute();
    while (my @row = $sth->fetchrow_array) {
        print "$row[0] $row[1] $row[2]\n";
    }
 
    $sth->finish();
    $dbh->disconnect();
}
 
analyze_aws("aws.db", "aws.txt");

역시 CentOS 5.10에서 테스트했고, 실행결과는 아래와 같이 나옵니다.

$ ./aws.pl
90 1991 3
91 1991 8
90 1992 24
91 1992 14

익명 사용자의 이미지

도움이 많이 되었습니다

chanik의 이미지

작성하시던 코드를 수정해봤습니다.
수정사항은 3가지 였고, [1],[2],[3]으로 주석 달아놨습니다.

#!/usr/bin/env perl
 
use strict;
use warnings;
use 5.010;
 
my($i,$point,$year,@sum,%sum,@arr);
 
for ($i=1991; $i<=1992;$i++) {
 
open my $fh, '<', "aws.txt";
while (<$fh>) {
    # [1] 데이터파일 첫줄은 "포인트 년월일 기온" 헤더이므로 건너뜀
    next if $. == 1;
    @arr=split;
        $point = $arr[0];
        $year = substr $arr[1], 0, 4;
            # [2] 비교문이 아닌 대입문으로 되어 있었음
            if ($year == $i){
                if ($arr[2] >= 18.0){
            $sum{$point}{$year}{d} += ($arr[2] - 18);
            }
    }
}
 
foreach(sort {$a<=>$b} keys(%sum)) {
    # [3] $year 를 $i 로 수정함
    say join("\t",$_, $i, $sum{$_}{$i}{d});
   }
}
chanik의 이미지

아래 코드는 외부의 하드코딩된 for loop을 없앤 것입니다.
계기가 생긴 김에 perl을 좀 살펴보느라고 해 봤습니다.

#!/usr/bin/env perl
 
use strict;
use warnings;
use 5.010;
 
my($point,$year,%sum,@arr);
 
open my $fh, '<', "aws.txt";
while (<$fh>) {
    next if $. == 1;   # 첫줄은 건너뜀
    @arr=split;
    $point = $arr[0];
    $year = substr $arr[1], 0, 4;
    if ($arr[2] >= 18.0){
        $sum{$year}{$point} += ($arr[2] - 18);
    }
}
 
foreach(sort {$a<=>$b} keys(%sum)) {
    $year = $_;
    foreach(sort {$a<=>$b} keys(%{ $sum{$year} })) {
        say join("\t", $_, $year, $sum{$year}{$_});
    }
}
익명 사용자의 이미지

감사합니다

익명 사용자의 이미지

감사합니다

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.