วิธีที่เร็วที่สุดในการนับจำนวนอักขระแต่ละตัวในไฟล์คืออะไร


121

ฉันต้องการนับตัวอักษร G's N's T's C's และ "-" ในไฟล์หรือตัวอักษรทุกตัวถ้าจำเป็นมีคำสั่ง Unix ด่วนในการทำเช่นนี้หรือไม่?


56
การนับเบสใน DNA
Indrek

12
ฉันรักคำถามนี้วิธีการและเครื่องมือต่าง ๆ มากมายที่ใช้ในการแก้ปัญหาเดียวกัน
Geek

10
เฮ้นี่คือสนามกอล์ฟโคฟ
Earlz

13
ถ้ามีใครสนใจรุ่น windows powershell:[System.IO.File]::ReadAllText("C:\yourfile.txt").ToCharArray() | Group-Object $_ | Sort Count -Descending
Guillaume86

4
ตกลงฉันคิดว่าฉันพบวิธี PS บริสุทธิ์:Get-Content "C:\eula.3082.txt" | % { $_.ToCharArray() } | Group-Object | Sort Count -Descending
Guillaume86

คำตอบ:


136

หากคุณต้องการความเร็วจริง:

echo 'int cache[256],x,y;char buf[4096],letters[]="tacgn-"; int main(){while((x=read(0,buf,sizeof buf))>0)for(y=0;y<x;y++)cache[(unsigned char)buf[y]]++;for(x=0;x<sizeof letters-1;x++)printf("%c: %d\n",letters[x],cache[letters[x]]);}' | gcc -w -xc -; ./a.out < file; rm a.out;

เป็น pseudo-one-liner ที่เร็วอย่างไม่น่าเชื่อ

การทดสอบอย่างง่ายแสดงให้เห็นว่าใน Core i7 CPU 870 @ 2.93GHz ของฉันนั้นนับได้เพียง 600MB / s:

$ du -h bigdna 
1.1G    bigdna

time ./a.out < bigdna 
t: 178977308
a: 178958411
c: 178958823
g: 178947772
n: 178959673
-: 178939837

real    0m1.718s
user    0m1.539s
sys     0m0.171s

แตกต่างจากโซลูชันที่เกี่ยวข้องกับการเรียงลำดับอันนี้ทำงานในหน่วยความจำคงที่ (4K) ซึ่งมีประโยชน์มากหากไฟล์ของคุณมีขนาดใหญ่กว่าแรมของคุณ

และแน่นอนว่ามีจารบีข้อศอกเล็กน้อยเราสามารถโกนทิ้งได้ 0.7 วินาที:

echo 'int cache[256],x,buf[4096],*bp,*ep;char letters[]="tacgn-"; int main(){while((ep=buf+(read(0,buf,sizeof buf)/sizeof(int)))>buf)for(bp=buf;bp<ep;bp++){cache[(*bp)&0xff]++;cache[(*bp>>8)&0xff]++;cache[(*bp>>16)&0xff]++;cache[(*bp>>24)&0xff]++;}for(x=0;x<sizeof letters-1;x++)printf("%c: %d\n",letters[x],cache[letters[x]]);}' | gcc -O2 -xc -; ./a.out < file; rm a.out;

ทำมากกว่า 1.1GB / s ใน:

real    0m0.943s
user    0m0.798s
sys     0m0.134s

สำหรับการเปรียบเทียบฉันทดสอบโซลูชันอื่น ๆ บางอย่างในหน้านี้ซึ่งดูเหมือนว่าจะมีสัญญาความเร็ว

sed/ awkวิธีการแก้ปัญหาทำให้ความพยายามที่กล้าหาญ แต่เสียชีวิตหลังจาก 30 วินาที ด้วย regex ง่าย ๆ ฉันคาดหวังว่านี่จะเป็น bug ใน sed (GNU sed เวอร์ชั่น 4.2.1):

$ time sed 's/./&\n/g' bigdna | awk '!/^$/{a[$0]++}END{for (i in a)print i,a[i];}' 
sed: couldn't re-allocate memory

real    0m31.326s
user    0m21.696s
sys     0m2.111s

วิธี Perl ดูเหมือนจะมีแนวโน้มเช่นกัน แต่ฉันยอมแพ้หลังจากใช้งานเป็นเวลา 7 นาที

time perl -e 'while (<>) {$c{$&}++ while /./g} print "$c{$_} $_\n" for keys %c' < bigdna 
^C

real    7m44.161s
user    4m53.941s
sys     2m35.593s

1
+1 สำหรับโซลูชันที่มีเหตุผลเมื่อมีข้อมูลจำนวนมากและไม่ใช่แค่จำนวนไบต์ ไฟล์อยู่ในแคชดิสก์ใช่มั้ย
Daniel Beck

2
สิ่งที่เรียบร้อยคือมันมีความซับซ้อนของ O (N) ในการประมวลผลและ O (1) ในหน่วยความจำ ท่อมักจะมี O (N log N) ในการประมวลผล (หรือแม้กระทั่ง O (N ^ 2)) และ O (N) ในหน่วยความจำ
Martin Ueding

73
คุณกำลังขยายคำจำกัดความของ "บรรทัดคำสั่ง" ค่อนข้างน้อย
gerrit

11
การดัดงอตามข้อกำหนดของคำถาม - ฉันอนุมัติ; p. superuser.com/a/486037/10165 <- บางคนใช้การวัดประสิทธิภาพและนี่คือตัวเลือกที่เร็วที่สุด
Journeyman Geek

2
+1 ฉันซาบซึ้งกับการใช้ C ในจุดที่ถูกต้อง
Jeff Ferland

119

grep -o foo.text -e A -e T -e C -e G -e N -e -|sort|uniq -c

จะทำเคล็ดลับเป็นหนึ่งซับ จำเป็นต้องมีคำอธิบายเล็กน้อย

grep -o foo.text -e A -e T -e C -e G -e N -e -greps ไฟล์ foo.text สำหรับตัวอักษร a และ g และอักขระ-สำหรับอักขระแต่ละตัวที่คุณต้องการค้นหา นอกจากนี้ยังพิมพ์อักขระหนึ่งบรรทัด

sortเรียงลำดับตามลำดับ ชุดนี้เป็นระยะสำหรับเครื่องมือถัดไป

uniq -cนับการเกิดซ้ำติดต่อกันของบรรทัดใด ๆ ในกรณีนี้เนื่องจากเรามีรายชื่อตัวละครที่เรียงลำดับเราจะได้รับการนับที่เรียบร้อยเมื่อตัวละครที่เราทำในตอนแรก

หาก foo.txt มีสตริงGATTACA-นี่คือสิ่งที่ฉันจะได้รับจากชุดคำสั่งนี้

[geek@atremis ~]$ grep -o foo.text -e A -e T -e C -e G -e N -e -|sort|uniq -c
      1 -
      3 A
      1 C
      1 G
      2 T

8
Bloody unix magic! : D
Pitto

27
ถ้ามีตัวอักษร CTAG- เพียงตัวเดียวในไฟล์ของคุณ regexp เองก็ไม่มีจุดหมายใช่ไหม? grep -o | จัดเรียง | uniq-c จะทำงานได้ดีอย่างเท่าเทียมกัน afaik
sylvainulg

7
+1 ฉันใช้ grep มา 25 ปีแล้วและไม่รู้เรื่อง-oเลย
LarsH

9
@JourneymanGeek: ปัญหานี้คือมันสร้างข้อมูลจำนวนมากที่ถูกส่งต่อไปยังการเรียงลำดับแล้ว มันจะถูกกว่าเพื่อให้โปรแกรมแยกวิเคราะห์ตัวละครแต่ละตัว ดูคำตอบของ Dave สำหรับคำตอบ O (1) แทนคำตอบความซับซ้อนของหน่วยความจำ O (N)
Martin Ueding

2
@Pitto Native Windows สร้าง coreutils อย่างกว้างขวาง - เพียงแค่ถาม Google หรือ
somesuch

46

ลองอันนี้แรงบันดาลใจจากคำตอบของ @ Journeyman

grep -o -E 'A|T|C|G|N|-' foo.txt | sort | uniq -c

ที่สำคัญคือการรู้เกี่ยวกับ-o ตัวเลือกสำหรับ grep สิ่งนี้จะแยกการจับคู่ขึ้นเพื่อให้แต่ละบรรทัดเอาต์พุตสอดคล้องกับอินสแตนซ์เดียวของรูปแบบแทนที่จะเป็นทั้งบรรทัดสำหรับบรรทัดใด ๆ ที่ตรงกัน จากความรู้นี้สิ่งที่เราต้องมีคือรูปแบบการใช้งานและวิธีการนับจำนวนบรรทัด การใช้ regex เราสามารถสร้างรูปแบบที่แยกจากกันซึ่งจะตรงกับตัวละครที่คุณพูดถึง:

A|T|C|G|N|-

ซึ่งหมายความว่า "จับคู่ A หรือ T หรือ C หรือ G หรือ N หรือ -" คู่มืออธิบายไวยากรณ์ของนิพจน์ทั่วไปที่คุณสามารถใช้ได้

ตอนนี้เรามีเอาต์พุตที่มีลักษณะดังนี้:

$ grep -o -E 'A|T|C|G|N|-' foo.txt 
A
T
C
G
N
-
-
A
A
N
N
N

ขั้นตอนสุดท้ายของเราคือการรวมและนับจำนวนบรรทัดที่คล้ายกันซึ่งสามารถทำได้โดยใช้ a sort | uniq -cในคำตอบของ @ Journeyman การเรียงลำดับให้ผลลัพธ์ดังนี้:

$ grep -o -E 'A|T|C|G|N|-' foo.txt | sort
-
-
A
A
A
C
G
N
N
N
N
T

ซึ่งเมื่อส่งผ่านuniq -cในที่สุดก็คล้ายกับสิ่งที่เราต้องการ:

$ grep -o -E 'A|T|C|G|N|-' foo.txt | sort | uniq -c
      2 -
      3 A
      1 C
      1 G
      4 N
      1 T

ภาคผนวก: ถ้าคุณต้องการที่จะรวมจำนวน A, C, G, N, T, และ - ตัวอักษรในไฟล์ที่คุณสามารถท่อส่งออก grep ผ่านแทนwc -l sort | uniq -cมีสิ่งต่าง ๆ มากมายที่คุณสามารถนับได้ด้วยการปรับเปลี่ยนวิธีนี้เพียงเล็กน้อยเท่านั้น


ฉันต้องเจาะรูบิททอลที่เป็นแกนกลางและ regex นี่ค่อนข้างหรูหรากว่าของฉัน p
Journeyman Geek

2
@ JourneymanGeek: การเรียน regex นั้นคุ้มค่ากับปัญหาเพราะมันมีประโยชน์สำหรับหลาย ๆ อย่าง เพียงแค่เข้าใจข้อ จำกัด ของมันและไม่ละเมิดอำนาจโดยพยายามที่จะทำสิ่งที่อยู่นอกขอบเขตของ regexes มีคุณลักษณะเช่นพยายามที่จะแยก XHTML
crazy2be

20
grep -o '[ATCGN-]' อาจอ่านได้ง่ายขึ้นที่นี่
sylvainulg

14

หนึ่งซับนับตัวอักษรทั้งหมดโดยใช้ Python:

$ python -c "import collections, pprint; pprint.pprint(dict(collections.Counter(open('FILENAME_HERE', 'r').read())))"

... สร้างเอาต์พุตที่เป็นมิตรของ YAML เช่นนี้:

{'\n': 202,
 ' ': 2153,
 '!': 4,
 '"': 62,
 '#': 12,
 '%': 9,
 "'": 10,
 '(': 84,
 ')': 84,
 '*': 1,
 ',': 39,
 '-': 5,
 '.': 121,
 '/': 12,
 '0': 5,
 '1': 7,
 '2': 1,
 '3': 1,
 ':': 65,
 ';': 3,
 '<': 1,
 '=': 41,
 '>': 12,
 '@': 6,
 'A': 3,
 'B': 2,
 'C': 1,
 'D': 3,
 'E': 25}

เป็นที่น่าสนใจที่จะเห็นว่าเวลาส่วนใหญ่ของ Python สามารถเอาชนะได้อย่างง่ายดายแม้กระทั่งทุบตีในแง่ของความชัดเจนของรหัส



10

หลังจากใช้ UNIX มาสองสามปีคุณจะมีความเชี่ยวชาญในการเชื่อมโยงการดำเนินงานขนาดเล็กจำนวนมากเข้าด้วยกันเพื่อให้บรรลุภารกิจการกรองและการนับที่หลากหลาย ทุกคนมี style-- ของตัวเองบางคนชอบawkและsedบางส่วนเหมือนและcut trนี่คือวิธีที่ฉันจะทำ:

ในการประมวลผลชื่อไฟล์เฉพาะ:

 od -a FILENAME_HERE | cut -b 9- | tr " " \\n | egrep -v "^$" | sort | uniq -c

หรือเป็นตัวกรอง:

 od -a | cut -b 9- | tr " " \\n | egrep -v "^$" | sort | uniq -c

มันได้ผลเช่นนี้:

  1. od -a แยกไฟล์ออกเป็นอักขระ ASCII
  2. cut -b 9-กำจัดคำนำหน้าodทำให้
  3. tr " " \\n แปลงช่องว่างระหว่างอักขระเป็นบรรทัดใหม่เพื่อให้มีอักขระหนึ่งตัวต่อบรรทัด
  4. egrep -v "^$" กำจัดทุกบรรทัดว่างที่สร้างขึ้น
  5. sort รวบรวมตัวอย่างของตัวละครแต่ละตัวด้วยกัน
  6. uniq -c นับจำนวนการทำซ้ำของแต่ละบรรทัด

ฉันเลี้ยงมันว่า "สวัสดีโลก!" ตามด้วยการขึ้นบรรทัดใหม่และได้รับสิ่งนี้:

  1 ,
  1 !
  1 d
  1 e
  1 H
  3 l
  1 nl
  2 o
  1 r
  1 sp
  1 w

9

sedส่วนหนึ่งอยู่บนพื้นฐานของคำตอบ @ คุรุนี่เป็นอีกวิธีหนึ่งที่ใช้uniqคล้ายกับวิธีการแก้ปัญหาเดวิด Schwartz'

$ cat foo
aix
linux
bsd
foo
$ sed 's/\(.\)/\1\n/g' foo | sort | uniq -c
4 
1 a
1 b
1 d
1 f
2 i
1 l
1 n
2 o
1 s
1 u
2 x

1
ใช้[[:alpha:]]มากกว่า.ในsedเพียงตรงกับตัวอักษรและไม่ขึ้นบรรทัดใหม่
Claudius

1
[[:alpha:]]จะล้มเหลวหากคุณพยายามจับคู่สิ่งที่ชอบ-ซึ่งถูกกล่าวถึงในคำถาม
Izkata

แก้ไข. มันอาจจะดีกว่าที่จะเพิ่มการแสดงออกที่สองเพื่อ sed sed -e 's/[^ATCGN-]//g' -e 's/\([ATCGN-]\)/\1\n/g' foo | sort | uniq -cแรกกรองทุกอย่างอื่นแล้วอย่างชัดเจนตรงกับตัวละครที่ต้องการ: อย่างไรก็ตามฉันไม่ทราบวิธีกำจัดบรรทัดใหม่ที่นั่น: \
คาร์ดินัล

7

คุณสามารถรวมgrepและwcทำสิ่งนี้:

grep -o 'character' file.txt | wc -w

grepค้นหาไฟล์ที่ระบุสำหรับข้อความที่ระบุและ-oตัวเลือกบอกให้พิมพ์เฉพาะการจับคู่ที่แท้จริง (เช่นอักขระที่คุณกำลังค้นหา) แทนที่จะเป็นค่าเริ่มต้นซึ่งจะพิมพ์แต่ละบรรทัดที่ข้อความค้นหาเป็น พบได้ที่

wcพิมพ์จำนวนไบต์คำและบรรทัดสำหรับแต่ละไฟล์หรือในกรณีนี้เอาต์พุตของgrepคำสั่ง -wตัวเลือกที่จะบอกว่ามันจะนับคำแต่ละคำเป็นปรากฏการณ์ที่เกิดขึ้นของตัวละครการค้นหาของคุณ แน่นอนว่า-lตัวเลือก (ซึ่งนับจำนวนบรรทัด) ก็ใช้ได้เช่นกันเนื่องจากจะgrepพิมพ์อักขระการค้นหาของคุณในแต่ละบรรทัดแยกกัน

ในการทำเช่นนี้สำหรับจำนวนอักขระพร้อมกันให้ใส่อักขระในอาร์เรย์และวนรอบ:

chars=(A T C G N -)
for c in "${chars[@]}"; do echo -n $c ' ' && grep -o $c file.txt | wc -w; done

ตัวอย่าง: สำหรับไฟล์ที่มีสตริงTGC-GTCCNATGCGNNTCACANN-ผลลัพธ์จะเป็น:

A  3
T  4
C  6
G  4
N  5
-  2

สำหรับข้อมูลเพิ่มเติมโปรดดูที่และman grepman wc


ข้อเสียของวิธีการนี้ในฐานะผู้ใช้ Journeyman Geek บันทึกไว้ด้านล่างในความคิดเห็นคือgrepจะต้องมีการเรียกใช้ครั้งเดียวสำหรับตัวละครแต่ละตัว ขึ้นอยู่กับว่าไฟล์ของคุณมีขนาดใหญ่เท่าไร ในทางกลับกันเมื่อทำเช่นนี้จะเป็นการง่ายกว่าที่จะเห็นตัวละครที่กำลังค้นหาอย่างรวดเร็วและเพื่อเพิ่ม / ลบพวกเขาเนื่องจากพวกเขาอยู่ในบรรทัดที่แยกต่างหากจากส่วนที่เหลือของรหัส


3
พวกเขาต้องการทำซ้ำตาม charecter ที่พวกเขาต้องการ ... ฉันจะเพิ่ม ฉันสาบานได้ว่าจะมีวิธีแก้ปัญหาที่หรูหรากว่า แต่ก็ต้องการการกระตุ้นมากกว่านี้ p
Journeyman Geek

@ JourneymanGeek จุดที่ดี วิธีการหนึ่งที่สปริงใจคือการใส่อักขระในอาร์เรย์และวนรอบมัน ฉันอัพเดทโพสต์แล้ว
Indrek

IMO ที่ซับซ้อนเกินไป เพียงใช้ grep -ea -et และอื่น ๆ หากคุณใส่มันเข้าไปในอาร์เรย์และวนรอบมันคุณจะไม่ต้องวิ่งผ่านวงจร grep หนึ่งครั้งต่อตัวละครใช่ไหม
Geek

@JourneymanGeek คุณอาจพูดถูก uniq -cดูเหมือนว่าจะเป็นวิธีที่ดีกว่าในการจัดรูปแบบผลลัพธ์ที่ดี ฉันไม่ guru * ระวังดังกล่าวข้างต้นเป็นเพียงสิ่งที่ฉันจัดการเพื่อใส่กันจากความรู้ที่ จำกัด ของฉันและบางหน้าคน :)
Indrek

ดังนั้นฉัน; p และหนึ่งในคำศัพท์สุดท้ายของฉันที่เกี่ยวข้องกับการเรียงลำดับรายการสมุดที่อยู่ประมาณ 5000 รายการและ uniq ทำให้ง่ายขึ้นมาก
Geek

7

การใช้บรรทัดลำดับจาก 22hgp10a.txt ความแตกต่างของเวลาระหว่าง grep และ awk บนระบบของฉันทำให้การใช้ awk เป็นวิธีที่จะไป ...

[แก้ไข]: หลังจากที่ได้เห็นโซลูชันที่คอมไพล์ของเดฟลืมไปด้วยเช่นกันเมื่อเขาเสร็จในเวลาประมาณ 0.1 วินาทีในไฟล์นี้สำหรับการนับจำนวนตัวพิมพ์เล็กและใหญ่

# A nice large sample file.
wget http://gutenberg.readingroo.ms/etext02/22hgp10a.txt

# Omit the regular text up to the start `>chr22` indicator.
sed -ie '1,/^>chr22/d' 22hgp10a.txt

sudo test # Just get sudo setup to not ask for password...

# ghostdog74 answered a question <linked below> about character frequency which
# gave me all case sensitive [ACGNTacgnt] counts in ~10 seconds.
sudo chrt -f 99 /usr/bin/time -f "%E elapsed, %c context switches" \
awk -vFS="" '{for(i=1;i<=NF;i++)w[$i]++}END{for(i in w) print i,w[i]}' 22hgp10a.txt

# The grep version given by Journeyman Geek took a whopping 3:41.47 minutes
# and yielded the case sensitive [ACGNT] counts.
sudo chrt -f 99 /usr/bin/time -f "%E elapsed, %c context switches" \
grep -o foo.text -e A -e T -e C -e G -e N -e -|sort|uniq -c

รุ่นตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ของ ghostdog นั้นเสร็จสิ้นใน ~ 14 วินาที

มีการอธิบายคำตอบที่ถูกต้องสำหรับคำถามนี้
การเปรียบเทียบเป็นไปตามคำตอบที่ได้รับการยอมรับสำหรับคำถามนี้
คำตอบที่ได้รับการยอมรับโดย ghostdog74 คือคำถามนี้


1
คุณสามารถs/cache[letters[x]]/cache[letters[x]]+cache[toupper(letters[x])]ขุดเพื่อทำให้ตัวพิมพ์เล็กและตัวพิมพ์เล็กตายโดยไม่กระทบกับความเร็ว
เดฟ

6

ฉันคิดว่าการใช้งานที่เหมาะสมหลีกเลี่ยงการเรียงลำดับ แต่เนื่องจากมันเป็นความคิดที่ดีที่จะอ่านทุกอย่าง 4 ครั้งฉันคิดว่าเราสามารถสร้างสตรีมที่ผ่านตัวกรอง 4 ตัวหนึ่งตัวสำหรับแต่ละอักขระซึ่งถูกกรองออกและที่ความยาวของกระแสถูกคำนวณด้วย

time cat /dev/random | tr -d -C 'AGCTN\-' | head -c16M >dna.txt
real    0m5.797s
user    0m6.816s
sys     0m1.371s

$ time tr -d -C 'AGCTN\-' <dna.txt | tee >(wc -c >tmp0.txt) | tr -d 'A' | 
tee >(wc -c >tmp1.txt) | tr -d 'G' | tee >(wc -c >tmp2.txt) | tr -d 'C' | 
tee >(wc -c >tmp3.txt) | tr -d 'T' | tee >(wc -c >tmp4.txt) | tr -d 'N' | 
tee >(wc -c >tmp5.txt) | tr -d '\-' | wc -c >tmp6.txt && cat tmp[0-6].txt

real    0m0.742s
user    0m0.883s
sys     0m0.866s

16777216
13983005
11184107
8387205
5591177
2795114
0

ผลรวมสะสมจะอยู่ใน tmp [0-6] .txt .. ดังนั้นการทำงานยังอยู่ระหว่างดำเนินการ

วิธีนี้มีเพียง 13 ท่อซึ่งแปลงเป็นหน่วยความจำน้อยกว่า 1 Mb
แน่นอนทางออกที่ฉันชอบคือ:

time cat >f.c && gcc -O6 f.c && ./a.out
# then type your favourite c-program
real    0m42.130s

trนี่คือการใช้ดีมากของ
adavid

4

ฉันไม่ทราบเกี่ยวกับuniqหรือเกี่ยวกับgrep -oแต่เนื่องจากความคิดเห็นของฉันใน @JourneymanGeek และ @ crazy2be มีการสนับสนุนเช่นนี้บางทีฉันควรเปลี่ยนมันให้กลายเป็นของตัวเอง:

หากคุณรู้ว่ามีเพียงอักขระ "ดี" (ตัวที่คุณต้องการนับ) ในไฟล์ของคุณคุณสามารถไปได้

grep . -o YourFile | sort | uniq -c

หากมีเพียงบางตัวอักษรจะต้องนับและอื่น ๆ ไม่ได้ (เช่นตัวคั่น)

grep '[ACTGN-]' YourFile | sort | uniq -c

คนแรกใช้ตัวแทนสัญลักษณ์การแสดงออกปกติ.ซึ่งตรงกับตัวละครเดียวใด ๆ ตัวที่สองใช้ 'ชุดของอักขระที่ยอมรับ' โดยไม่มีลำดับที่เฉพาะเจาะจงยกเว้นว่า-จะต้องมาครั้งสุดท้าย ( A-Cถูกตีความว่าเป็น 'ตัวละครใด ๆ ระหว่างAและC) จำเป็นต้องใช้เครื่องหมายคำพูดในกรณีนั้นเพื่อให้เชลล์ของคุณไม่พยายามขยายเพื่อตรวจสอบไฟล์อักขระเดี่ยวหากมี (และสร้างข้อผิดพลาด "ไม่ตรงกัน" หากไม่มี)

โปรดทราบว่า "การเรียงลำดับ" ยังมีการ-uตั้งค่าสถานะแบบ nique เพื่อให้รายงานได้เพียงครั้งเดียว แต่ไม่มีการตั้งค่าสถานะร่วมเพื่อนับการซ้ำซ้อนดังนั้นจึงuniqจำเป็นต้องมี


-ไม่จำเป็นต้องมาก่อนหากคุณหลบหนีด้วยแบ็กสแลช: '[A\-CTGN]'ควรใช้งานได้ดี
Indrek

2

โง่หนึ่ง:

tr -cd ATCGN- | iconv -f ascii -t ucs2 | tr '\0' '\n' | sort | uniq -c
  • trเพื่อลบ ( -d) ตัวละครทั้งหมดยกเว้น ( -c) ATCGN-
  • iconv เพื่อแปลงเป็น ucs2 (UTF16 จำกัด ที่ 2 ไบต์) เพื่อเพิ่ม 0 ไบต์หลังจากทุกไบต์
  • อีกอันtrเพื่อแปลอักขระ NUL เหล่านั้นเป็น NL ตอนนี้ตัวละครทุกตัวอยู่ในสายของตัวเอง
  • sort | uniq -cเพื่อนับแต่ละบรรทัดuniq

นี่เป็นทางเลือกแทน-oตัวเลือก grep ที่ไม่ได้มาตรฐาน (GNU)


คุณช่วยอธิบายสั้น ๆ เกี่ยวกับคำสั่งและตรรกะที่นี่ได้ไหม?
Andrew Lambert

2
time $( { tr -cd ACGTD- < dna.txt | dd | tr -d A | dd | tr -d C | dd | tr -d G |
dd | tr -d T | dd | tr -d D | dd | tr -d - | dd >/dev/null; } 2>tmp ) &&
grep byte < tmp | sort -r -g | awk '{ if ((s-$0)>=0) { print s-$0} s=$0 }'

รูปแบบผลลัพธ์ไม่ดีที่สุด ...

real    0m0.176s
user    0m0.200s
sys     0m0.160s
2069046
2070218
2061086
2057418
2070062
2052266

ทฤษฎีการทำงาน:

  • $ ({คำสั่ง | คำสั่ง} 2> tmp) เปลี่ยนเส้นทางstderrของกระแสไปยังไฟล์ชั่วคราว
  • dd เอาต์พุต stdin ถึง stdout และเอาต์พุตจำนวนไบต์ที่ส่งผ่านไปยัง stderr
  • tr -d กรองอักขระทีละตัว
  • grep และ sort จะกรองเอาต์พุตของ dd ไปยังลำดับจากมากไปน้อย
  • awk คำนวณความแตกต่าง
  • การเรียงลำดับจะใช้เฉพาะในขั้นตอนหลังการประมวลผลเพื่อจัดการกับความไม่แน่นอนของลำดับการออกของอินสแตนซ์ของ dd

ความเร็วดูเหมือนจะเป็น 60MBps +


การปรับปรุง: กำจัด tmp หรือไม่ ใช้ 'วาง' เพื่อพิมพ์จดหมายที่เกี่ยวข้องหรือไม่
Aki Suihkonen

1

ไฟล์ตัวอย่าง:

$ cat file
aix
unix
linux

คำสั่ง:

$ sed 's/./&\n/g' file | awk '!/^$/{a[$0]++}END{for (i in a)print i,a[i];}'
u 2
i 3
x 3
l 1
n 2
a 1

-1 สำหรับการขาดความชัดเจนและสำหรับการโพสต์หนึ่งซับโดยไม่มีคำอธิบาย AFAIK นี่อาจเป็น fork fork
PPC

1

เป็นการรวมกันของคนอื่น

chars='abcdefghijklmnopqrstuvwxyz-'
grep -o -i "[$chars]" foo|sort | uniq -c

เพิ่ม| sort -nrเพื่อดูผลลัพธ์ตามลำดับความถี่


1

คำตอบสั้น ๆ :

หากสถานการณ์อนุญาตให้เปรียบเทียบขนาดไฟล์ของชุดอักขระต่ำเป็นหนึ่งโดยไม่มีอักขระเพื่อรับออฟเซ็ตและนับเป็นไบต์

อ่า แต่รายละเอียดยุ่งเหยิง:

นี่คือตัวละคร Ascii ทั้งหมด หนึ่งไบต์ต่อ แน่นอนว่าไฟล์มีข้อมูลเมตาเพิ่มเติมสำหรับสิ่งต่าง ๆ ที่ใช้โดยระบบปฏิบัติการและแอปที่สร้างขึ้น ในกรณีส่วนใหญ่ฉันคาดว่าสิ่งเหล่านี้จะใช้พื้นที่จำนวนเท่ากันโดยไม่คำนึงถึงข้อมูลเมตา แต่ฉันจะพยายามรักษาสถานการณ์ที่เหมือนกันเมื่อคุณทดสอบวิธีแรกแล้วตรวจสอบว่าคุณมีออฟเซ็ตคงที่ก่อนไม่ต้องกังวล gotcha อื่นคือการแบ่งบรรทัดโดยทั่วไปเกี่ยวข้องกับอักขระช่องว่างสีขาวสองตัวและแท็บหรือช่องว่างใด ๆ จะเป็นหนึ่ง หากคุณมั่นใจได้ว่าจะมีสิ่งเหล่านี้ปรากฏขึ้นและไม่มีทางรู้จำนวนล่วงหน้าฉันจะหยุดอ่านเดี๋ยวนี้

มันอาจดูเหมือนว่ามีข้อ จำกัด มากมาย แต่ถ้าคุณสามารถสร้างมันขึ้นมาได้อย่างง่ายดายสิ่งนี้ทำให้ฉันเป็นวิธีที่ง่ายที่สุด / ดีที่สุดถ้าคุณมีสิ่งเหล่านี้ให้ดู (ซึ่งน่าจะเป็นของ DNA) การตรวจสอบความยาวและความยาวของไฟล์จำนวนหนึ่งและการลบค่าคงที่นั้นจะเร็วกว่าการรัน grep (หรือคล้ายกัน) ในทุก ๆ ไฟล์

ถ้า:

  • สิ่งเหล่านี้เป็นสตริงที่ไม่สามารถจำแนกได้ง่ายในไฟล์ข้อความล้วน
  • พวกเขาอยู่ในประเภทไฟล์ที่เหมือนกันที่สร้างขึ้นโดยวานิลลาที่ไม่ใช่การจัดรูปแบบตัวแก้ไขข้อความเช่น Scite (การวางไม่เป็นไรตราบใดที่คุณตรวจสอบช่องว่าง / ผลตอบแทน) หรือโปรแกรมพื้นฐานบางคนเขียน

และสองสิ่งที่อาจไม่สำคัญ แต่ฉันจะทำการทดสอบก่อน

  • ชื่อไฟล์มีความยาวเท่ากัน
  • ไฟล์อยู่ในไดเรกทอรีเดียวกัน

ลองค้นหาออฟเซ็ตโดยทำดังต่อไปนี้:

เปรียบเทียบไฟล์ที่ว่างเปล่ากับไฟล์ที่มีตัวละครที่มนุษย์นับได้ง่าย ๆ สองถึงสามตัวและอีกสองสามตัว หากการลบไฟล์เปล่าออกจากไฟล์อีกสองไฟล์นั้นให้คุณนับจำนวนไบต์ที่ตรงกับจำนวนตัวอักษรแสดงว่าคุณทำเสร็จแล้ว ตรวจสอบความยาวไฟล์และลบจำนวนเงินที่ว่างเปล่านั้น หากคุณต้องการลองหาไฟล์หลายบรรทัดตัวแก้ไขส่วนใหญ่จะแนบอักขระพิเศษขนาดหนึ่งไบต์สองตัวเพื่อแบ่งบรรทัดเนื่องจากไมโครซอฟท์มักจะถูกละเว้น แต่อย่างน้อยคุณต้อง grep สำหรับ white-space chars ในกรณีนี้ คุณอาจทำทุกอย่างด้วย grep


1

Haskellวิธี:

import Data.Ord
import Data.List
import Control.Arrow

main :: IO ()
main = interact $
  show . sortBy (comparing fst) . map (length &&& head) . group . sort

มันทำงานได้เช่นนี้:

112123123412345
=> sort
111112222333445
=> group
11111 2222 333 44 5
=> map (length &&& head)
(5 '1') (4 '2') (3 '3') (2 '4') (1,'5')
=> sortBy (comparing fst)
(1 '5') (2 '4') (3 '3') (4 '2') (5 '1')
=> one can add some pretty-printing here
...

รวบรวมและใช้:

$ ghc -O2 q.hs
[1 of 1] Compiling Main             ( q.hs, q.o )
Linking q ...
$ echo 112123123412345 | ./q
[(1,'\n'),(1,'5'),(2,'4'),(3,'3'),(4,'2'),(5,'1')]%       
$ cat path/to/file | ./q
...

อาจไม่ดีสำหรับไฟล์ขนาดใหญ่


1

แฮก Perl อย่างรวดเร็ว:

perl -nle 'while(/[ATCGN]/g){$a{$&}+=1};END{for(keys(%a)){print "$_:$a{$_}"}}'
  • -n: วนซ้ำบรรทัดอินพุต แต่ไม่ต้องพิมพ์อะไรเลย
  • -l: ตัดหรือเพิ่มตัวแบ่งบรรทัดโดยอัตโนมัติ
  • while: วนซ้ำทุกสัญลักษณ์ที่คุณร้องขอในบรรทัดปัจจุบัน
  • END: ในตอนท้ายพิมพ์ผลลัพธ์
  • %a: แฮชที่เก็บค่าไว้

อักขระที่ไม่เกิดขึ้นจะไม่รวมอยู่ในผลลัพธ์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.