सवाल फ़ाइल में प्रत्येक वर्ण की संख्या को गिनने का सबसे तेज़ तरीका क्या है?


मैं ए के टी के सी के जी के एन और "-" अक्षरों को फाइल में गिनना चाहता हूं, या यदि आवश्यक हो तो प्रत्येक अक्षर, क्या ऐसा करने के लिए एक त्वरित यूनिक्स कमांड है?


120


मूल


डीएनए तारों में गिनती अड्डों? - Indrek
मुझे इस सवाल से बहुत प्यार है, एक ही समस्या को हल करने के लिए उपयोग किए जाने वाले कई अलग-अलग दृष्टिकोण और औजार। - Journeyman Geek♦
हे, यह सीमा रेखा कोड-गोल्फ है - Earlz
अगर किसी को विंडोज़ पावरहेल संस्करण में दिलचस्पी है: [System.IO.File]::ReadAllText("C:\yourfile.txt").ToCharArray() | Group-Object $_ | Sort Count -Descending - Guillaume86
ठीक है मुझे लगता है कि मुझे शुद्ध पीएस रास्ता मिला है: Get-Content "C:\eula.3082.txt" | % { $_.ToCharArray() } | Group-Object | Sort Count -Descending - Guillaume86


जवाब:


यदि आप कुछ वास्तविक गति चाहते हैं:

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;

एक अविश्वसनीय रूप से तेज़ छद्म-एक-लाइनर है।

एक साधारण परीक्षण से पता चलता है कि मेरे कोर i7 CPU 870 @ 2.93GHz पर यह सिर्फ 600 एमबी / एस से अधिक है:

$ 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

सॉर्टिंग से जुड़े समाधानों के विपरीत, यह स्थिर (4 के) मेमोरी में चलता है, जो बहुत उपयोगी है, अगर आपकी फ़ाइल आपके रैम से कहीं अधिक है।

और, निश्चित रूप से कोहनी ग्रीस के थोड़ा सा के साथ, हम 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.1 जीबी / एस खत्म हो गया है:

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

तुलना के लिए, मैंने इस पृष्ठ पर कुछ अन्य समाधानों का परीक्षण किया जो कि किसी प्रकार का स्पीड वादा था।

sed/awk समाधान ने एक बहादुर प्रयास किया, लेकिन 30 सेकंड के बाद मृत्यु हो गई। इस तरह के एक साधारण regex के साथ, मैं उम्मीद करता हूँ कि यह एक बग (जीएनयू 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

पर्ल विधि भी आशाजनक लग रही थी, लेकिन मैंने 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

135



+1 एक सॉन समाधान के लिए जब यह बहुत सारे डेटा है, न केवल बाइट्स के कुछ मुट्ठी भर। फाइलें डिस्क कैश में हैं, है ना? - Daniel Beck♦
साफ बात यह है कि इसमें प्रसंस्करण में ओ (एन) की जटिलता है और ओ (1) स्मृति में है। पाइप आमतौर पर प्रसंस्करण (या यहां तक ​​कि ओ (एन ^ 2)) और ओ (एन) स्मृति में ओ (एन लॉग एन) है। - Martin Ueding
हालांकि, आप "कमांड लाइन" की परिभाषा को काफी हद तक खींच रहे हैं। - gerrit
प्रश्न की आवश्यकताओं के महाकाव्य झुकाव - मैं मंजूरी देता हूं; पी। superuser.com/a/486037/10165 <- किसी ने बेंचमार्क चलाया, और यह है सबसे तेज़ विकल्प - Journeyman Geek♦
+1 मैं सही जगहों पर सी के कुछ अच्छे उपयोग की सराहना करता हूं। - Jeff Ferland


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 - ए और जी और चरित्र अक्षरों के लिए फ़ाइल foo.text फ़ाइल greps - प्रत्येक चरित्र के लिए आप खोजना चाहते हैं। यह इसे एक चरित्र को एक पंक्ति भी प्रिंट करता है।

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

118



खूनी यूनिक्स जादू! : डी - Pitto
अगर आपकी फाइलों में केवल CTAG- वर्ण हैं, तो regexp स्वयं व्यर्थ हो जाता है, है ना? grep -o। | सॉर्ट | uniq -c समान रूप से अच्छी तरह से काम करेगा, afaik। - sylvainulg
+1 मैं 25 साल के लिए grep का उपयोग कर रहा हूं और इसके बारे में नहीं पता था -o। - LarsH
@JourneymanGeek: इसके साथ समस्या यह है कि यह बहुत सारे डेटा उत्पन्न करता है जिसे क्रमबद्ध करने के लिए अग्रेषित किया जाता है। कार्यक्रम को प्रत्येक चरित्र को पार्स करना सस्ता होगा। ओ (1) के बजाय ओ (एन) स्मृति जटिलता उत्तर के लिए डेव का जवाब देखें। - Martin Ueding
@Pitto मूल विंडोज़ कोरूटल्स का निर्माण व्यापक रूप से उपलब्ध है - बस Google या किसी से पूछें - OrangeDog


@ जर्नीमैन के जवाब से प्रेरित, इसे आजमाएं।

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

कुंजी के बारे में जानना है grep के लिए -o विकल्प। यह मैच को विभाजित करता है, ताकि प्रत्येक आउटपुट लाइन मिलान की किसी भी पंक्ति के लिए पूरी लाइन की बजाय पैटर्न के एक उदाहरण के अनुरूप हो। इस ज्ञान को देखते हुए, हमें केवल एक पैटर्न का उपयोग करने की आवश्यकता है, और लाइनों की गिनती करने का एक तरीका है। रेगेक्स का उपयोग करके, हम एक विचित्र पैटर्न बना सकते हैं जो आपके द्वारा वर्णित किसी भी वर्ण से मेल खाएगा:

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

हमारा अंतिम चरण सभी समान लाइनों को मर्ज करना और गिनना है, जिसे आसानी से पूरा किया जा सकता है sort | uniq -c, @ जर्नीमैन के जवाब में। इस तरह हमें आउटपुट देता है:

$ 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

अनुपूरक: यदि आप फ़ाइल में ए, सी, जी, एन, टी, और - वर्णों की संख्या को कुल करना चाहते हैं, तो आप grep आउटपुट के माध्यम से पाइप कर सकते हैं wc -l के बजाय sort | uniq -c। इस दृष्टिकोण के लिए केवल कुछ मामूली संशोधन के साथ आप कई अलग-अलग चीजों की गणना कर सकते हैं।


45



मुझे वास्तव में उन खरगोशों में डूबने की ज़रूरत है जो कोरुटिल्स और रेगेक्स हैं। यह इसके लिए मेरी तुलना में कुछ और अधिक सुरुचिपूर्ण है; पी - Journeyman Geek♦
@JourneymanGeek: Learing regex परेशानी के लायक है, क्योंकि यह बहुत सी चीजों के लिए उपयोगी है। बस इसकी सीमाओं को समझें, और रेगेक्स कैपेबिलिट्स के दायरे से बाहर चीजों को करने का प्रयास करके शक्ति का दुरुपयोग न करें, जैसे एक्सएचटीएमएल पार्स करने की कोशिश कर रहा है। - crazy2be
grep -o '[ATCGN-]' यहां थोड़ा और अधिक पठनीय हो सकता है। - sylvainulg


एक लाइनर पाइथन का उपयोग कर सभी अक्षरों की गिनती:

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

... इस तरह एक वाईएएमएल अनुकूल उत्पादन का उत्पादन:

{'\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}

यह देखना दिलचस्प है कि कोड की स्पष्टता के मामले में पाइथन कितनी बार आसानी से हरा सकता है।


13





गुरु के समान awk तरीका:

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

11





यूनिक्स का उपयोग कुछ सालों से करने के बाद, आप विभिन्न फ़िल्टरिंग और गिनती कार्यों को पूरा करने के लिए कई छोटे संचालन को जोड़ने के लिए बहुत कुशल हो जाते हैं। हर किसी की अपनी शैली होती है - कुछ पसंद है 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

10





sed भाग पर आधारित है @ गुरु का जवाब, यहां एक और दृष्टिकोण का उपयोग कर रहा है uniq, डेविड श्वार्टज़ के समाधान के समान।

$ 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

9



उपयोग [[:alpha:]] बजाय . में sed केवल पात्रों से मेल खाते हैं, न कि न्यूलाइन। - Claudius
[[:alpha:]] अगर आप सामानों से मेल खाने की कोशिश भी कर रहे हैं तो असफल हो जाएंगे -, जिसमें सवाल में उल्लेख किया गया था - Izkata
सही बात। पहले सब कुछ बाहर फ़िल्टर करने के लिए sed में दूसरी अभिव्यक्ति जोड़ने के लिए यह अच्छा हो सकता है और फिर वांछित वर्णों पर स्पष्ट रूप से मेल खाता है: sed -e 's/[^ATCGN-]//g' -e 's/\([ATCGN-]\)/\1\n/g' foo | sort | uniq -c। हालांकि, मुझे नहीं पता कि न्यूलाइन को कैसे छुटकारा पाना है: \ - Claudius