2016-04-29 47 views
0

我真的希望你能幫上忙。我對(g)awk完全陌生,在過去的兩週裏我一直與它鬥爭。基於唯一ID轉置數據 - awk

我的原始文件如下 - 有一列有唯一的ID和另一個有唯一的名稱。隨後的專欄是各種課程,每個字段包含(如果不是空的話)每個課程和每個學生的標記。因此,每個學生只有一個,每道菜的標記:

Id Name  Course1 Course2 Course3 Course4 Course5 
1 John   55 
2 George           63 
4 Alex       64 
1 John         74 
3 Emma   63 
2 George    64 
4 Alex         60 
2 George   29     
3 Emma           69 
1 John     67 
3 Emma     80 
4 Alex   57 
2 George         91 
1 John       81 
1 John           34 
3 Emma       75 
2 George      89 
4 Alex           49 
3 Emma         78 
4 Alex     69 
5 TERRY     67 
6 HELEN       39 

這是我想達到什麼 - 轉數據,即商標的基礎上,唯一的ID,並把每個相應課程下面的標記象下面這樣:

Id Name  Course1 Course2 Course3 Course4 Course5 
1 John   55  69  64  60  49 
2 George  29  64  89  91  63 
3 Emma   63  80  75  78  69 
4 Alex   57  69  64  60  49 
5 TERRY     67 
6 HELLEN       39 

這是我管理迄今爲止獲得:

Id Name  Course1 Course2 Course3 Course4 Course5 
1 John   55    
2 George  29    
3 Emma   63    
4 Alex   57  
5 TERRY 
6 HELLEN  
1 John     69    
2 George    64    
3 Emma     80    
4 Alex     69    
5 TERRY     67 
6 HELLEN 
1 John       64 
2 George       89 
3 Emma       75 
4 Alex       64 
5 TERRY 
6 HELLEN       39 
             ...and so on 

這實在是有點棘手,對我來說,基於什麼我已經知道awk的實現(請注意,我不感興趣,Sed/Perl進行e.t.c.基於解決方案)。 如果是提供一些幫助(最好不是一個班輪),我可以要求具有一定的描述性,因爲我對該解決方案感興趣的程度與我在該方法本身一樣。

任何幫助將非常感激。

編輯 這裏是我寫到達最後階段的代碼(在那裏我卡住了)

#!/bin/bash 

files3="*.csv" 
for j in $files3 
do 
    #echo "processing $j..." 
    fi13=$(awk -F" " '(NR==1){field13=$13;}{print field13}' ./work1/test1YA.csv) 
    fi14=$(awk -F" " '(NR==1){field14=$14;}{print field14}' ./work1/test1YA.csv) 
    fi15=$(awk -F" " '(NR==1){field15=$15;}{print field15}' ./work1/test1YA.csv) 
    fi16=$(awk -F" " '(NR==1){field16=$16;}{print field16}' ./work1/test1YA.csv) 

# awk -F" " 'BEGIN{OFS=" ";RS="\n"}{print $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12}' "$j" >> ./work1/test2YA.csv 
    awk -F" " -v f13="$fi13" -v f14="$fi14" -v f15="$fi15" -v f16="$fi16" '{if($13==f13){$13=$6;$14=$15=$16=""}if($13==f14){$14=$6;$13=$15=$16=""}if($13==f15){$15=$6;$13=$14=$16=""}if($13==f16){$16=$6;$13=$14=$15=""}{print $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16}}' "$j" >> ./work1/test2YA.csv 

done; 

awk -F" " 'BEGIN{print "ID","Title","FirstName","MiddleName","LastName","FinalMarks","Status","Username","Campus","Code","Programme","Year","course1","course2","course3","course4"}{print}' ./work1/test2YA.csv >> ./work1/test3YA.csv 
+0

你能粘貼產生你的中途解決方案的代碼嗎? – choroba

回答

1

這裏是GNU awk的一個解決方案:

course.awk

BEGIN { # setup field width for constant field splitting 
     FIELDWIDTHS = "2 2 12 7 1 7 1 7 1 7 1 7" 
     # setup sort order (by id) 
     PROCINFO["sorted_in"] = "@ind_num_asc" 
     } 

NR == 1 { # print header 
      print 
      next 
     } 

     { 
     # add ids to names 
     names[ $1 ] = $3 

     # store under id and course number the mark if it is present 
     for(c = 1; c <= 5; c++) { 
      field = 2+ (c*2) 
      if($(field) !~ /^ *$/) { 
      marks[ $1, c ] = $(field) 
      } 
     } 
     } 

END { 
     # output 
     for(id in names) { 
      printf("%-4s%-12s%7s %7s %7s %7s %7s\n",id, names[ id ], marks[ id, 1], marks[ id, 2], marks[ id, 3], marks[ id, 4], marks[ id, 5]) 
     } 
     } 

像這樣使用它:awk -f course.awk your_file

,該輸入沒有製表符分隔的,但有固定的列寬化妝事實是有點unelegant:

使用 FIELDWIDTHS%Ns其中N是
  • 從FIELDWIDTHS
  • FIELDWIDTHS採取衍生考慮ID和名稱,課程1和課程2之間的空列,...
  • 檢查是否存在標記:if($(field) !~ /^ *$/)檢查字段是否不完全由空格組成。
+0

完美!解。 – Firefly

+0

謝謝大家的快速和詳細的答案。我接受@Lars Fiscer的回答,因爲他第一個回答了評論,並且我瞭解了FIELDWIDTHS和PROCINFO(似乎我不能爲接受的答案「投票」,因爲我的名聲太低,或者「重擊」了其他答案,因爲他們都非常有用)! 我不得不稍微修改我的需求的答案(我的源文件列表,我沒有需要fieldwidths,procinfo和printf)。 再次很多很多感謝大家。你真是太好了。 – Yiannis

+0

好的,我發現如何接受答案...... – Yiannis

0

這可能是AWK近似:

NR==1{ 
    for(x=1;x<=NF;x++) 
    { 
     head=head $x"\t"; 
    } 
    print head 
} 
NR>1{ 
    for(i=3;i<=NF;i++) 
    { 
     students[$1"\t"$2]=students[$1"\t"$2] "\t"$i; 
    } 
} 
END{ 
    for (stu in students) 
    { 
     print stu,students[stu]; 
    } 
} 

Id  Name Course1 Course2 Course3 Course4 Course5 
5  TERRY 67 
4  Alex 64  60  57  49  69 
1  John 55  74  67  81  34 
6  HELEN 39 
3  Emma 63  69  80  75  78 
2  George 63  64  29  91  89 
+0

根據輸出邏輯似乎是錯誤的。檢查TERRY和HELEN。 – karakfa

+0

@karakfa謝謝你,我知道海倫分數應該在「course3」欄中,Terry分數在「course2」欄中。這就是爲什麼我說這是一個近似值。 – Firefly

0

同樣的想法,也許簡單

$ awk 'BEGIN{ FIELDWIDTHS="16 8 8 8 8 8"} 
     NR==1{print;next} 
     NR>1{keys[$1]; 
      for(i=2;i<=6;i++) 
       {gsub(" ","",$i); 
       if($i) a[$1,i]=$i}} 
     END{for(k in keys) 
       {printf "%16s",k; 
       for(i=2;i<=6;i++) printf "%-8s",a[k,i]; 
       print ""}}' file 


Id Name  Course1 Course2 Course3 Course4 Course5 
3 Emma  63  80  75  78  69 
4 Alex  57  69  64  60  49 
6 HELEN      39 
5 TERRY    67 
1 John  55  67  81  74  34 
2 George  29  64  89  91  63 

您可以通過管道輸出排序以及對sort -n

... | sort -n 

Id Name  Course1 Course2 Course3 Course4 Course5 
1 John  55  67  81  74  34 
2 George  29  64  89  91  63 
3 Emma  63  80  75  78  69 
4 Alex  57  69  64  60  49 
5 TERRY    67 
6 HELEN      39 
0

用於FIELDWIDTHS,2D數組和sorted_in的GNU awk:

$ cat tst.awk 
NR==1 { 
    print 
    split($0,f,/\S+\s*/,s) 
    for (i=1;i in s;i++) { 
     w[i] = length(s[i]) 
     FIELDWIDTHS = FIELDWIDTHS (i>1?" ":"") w[i] 
    } 
    next 
} 
{ 
    sub(/\s*$/," ") 
    for (i=1;i<=NF;i++) { 
     if ($i ~ /\S/) { 
      val[$1][i] = $i 
     } 
    } 
} 
END { 
    PROCINFO["sorted_in"] = "@ind_num_asc" 
    for (id in val) { 
     for (i=1;i<=NF;i++) { 
      printf "%*s", w[i], val[id][i] 
     } 
     print "" 
    } 
} 

$ awk -f tst.awk file 
Id Name  Course1 Course2 Course3 Course4 Course5 
1 John   55  67  81  74  34 
2 George   29  64  89  91  63 
3 Emma   63  80  75  78  69 
4 Alex   57  69  64  60  49 
5 TERRY     67 
6 HELEN       39 
0

這是我對此的看法。這在純老awk中不起作用(不使用FIELDWIDTHS),並自動調整到不同數量的字段(即添加一個Course7列,你應該沒問題)。另外,您可以將其指向多個文件,並且它應該分別處理每個文件。

#!/usr/bin/awk -f 

# Initialize variables on the first record of each input file 
# (and also print the header) 
# 
FNR <= 1 { 
    print 
    delete name 
    delete score 
    next 
} 

# Process each line. 
# 
{ 
    id = substr($0, 0, 16) # 
    name[id]     # Store the unique identifier in an array 
    pos = 0     # 

    # Step through the score fields until we hit the end of the line, 
    # storing scores in another array. 
    do { 
    score[id, pos] += substr($0,17+pos*8,8) +0 
    printf("id='%s' pos=%s value=%s total=%s\n", id, pos, substr($0,17+pos*8,8)+0, score[id, pos]); 
    } while (17+(++pos)*8 < length()) 
} 

# Keep track of our maximum number of fields 
pos>max { max=pos } 

# Finally, generate our (randomly sorted) output. 
END { 
    for (id in name) {  # Step through the records... 
    printf("%-12s", id); 
    for (i=0; i<max; i++) { # Step through the fields... 
     if (score[id, i]==0) score[id, i]="" 
     printf("%-8s", score[id, i]); 
    } 
    printf("\n") 
    } 
} 

這有點長,但我認爲它更容易理解它的功能。