Help with counting string elements

Hi All,
I hv several files which have hundreds of lines each for example

>XYZ.abc01
NNNTCGGTNNNNNCCACACACMYACACACCCACACCCACSCARCAC

I'd like to exculde the first line beginning with ">" and then for the rest of the lines get a count for each string element. So for the above example I would like to get the following output:

A=11
C=19
G=2
M=1
N=8
R=1
S=1
T=2
Y=1
Length of XYZ.abc01=46

Can anyone enlighten on this and if there is a Perl way i'll appreciate that as I'm learning Perl
Cheers and hv a nice day :slight_smile:

I didn't try it (may require some fix) but maybe something like this

#!/bin/ksh
{ read a
while read a
do
    [[ -z $a ]] && continue
    b=${a#?}
    c=${a%$b}
    
    if [[ "$c" == '>' ]]
    then
        h=$b 
        continue
    else
        fold -w 1 <(echo $a) | sort | uniq -c | awk '{print $2"="$1}'
        echo "Length of $h=${#a}"
    fi
done } <infile

seems working :

# cat tst
dummy first line
>XYZ.abc01
NNNTCGGTNNNNNCCACACACMYACACACCCACACCCACSCARCAC
>XYYUIZ.abc01
NNNTCGGTCACACMYACACACCCACCACACMYACACACCCACNNNNNCCACACACMYACACACCCACACCCACSCARCAC
>XYYU.abc03
NNNTCGGTCACACMYACACACCCACCACACMYACACACCCACNCACCCACACCCACSCARCAC
>XYYUIZ.abc04
NNNTCGGTCACACMYACACACCCACCACACMYAACACACCCACACCCACSCARCAC
>XYYUIZ.abc05
NNNTCGGTCACACMYACACANNNCCACACACMYACACACCCACACCCACSCARCAC
>XYYUIZ.abc06
NNNTCGGTCACACMYACACAACACMYACACACCCACNNNNNCCACACACMYACACACCCACACCCACSCARCAC
>XYYUIZ.abc07
NNNTCGGTCACACMYACACACCACACACMYACACACCCACACCCACSCARCAC
# cat sc
#!/bin/ksh
{ read a
while read a
do
    [[ -z $a ]] && continue
    b=${a#?}
    c=${a%$b}

    if [[ "$c" == '>' ]]
    then
        h=$b
        continue
    else
        fold -w 1 <(echo $a) | sort | uniq -c | awk '{print $2"="$1}'
        echo "Length of $h=${#a}"
    fi
done } <tst
# ksh sc
A=11
C=19
G=2
M=1
N=8
R=1
S=1
T=2
Y=1
Length of XYZ.abc01=46
A=23
C=37
G=2
M=3
N=8
R=1
S=1
T=2
Y=3
Length of XYYUIZ.abc01=80
A=18
C=31
G=2
M=2
N=4
R=1
S=1
T=2
Y=2
Length of XYYU.abc03=63
A=17
C=26
G=2
M=2
N=3
R=1
S=1
T=2
Y=2
Length of XYYUIZ.abc04=56
A=16
C=24
G=2
M=2
N=6
R=1
S=1
T=2
Y=2
Length of XYYUIZ.abc05=56
A=22
C=32
G=2
M=3
N=8
R=1
S=1
T=2
Y=3
Length of XYYUIZ.abc06=74
A=16
C=24
G=2
M=2
N=3
R=1
S=1
T=2
Y=2
Length of XYYUIZ.abc07=53
#

Try:

perl -aF// -lne 'if ($.==1){s/^>//;$n=$_}else{for $i (@F){$h{$i}++;$l++}}END{for $i (keys %h){print "$i=$h{$i}"}print "Length of $n=$l"}' file

We could make the output less longer putting letter stats in one line by changing this in the previous scripts:

fold -w 1 <(echo $a) | sort | uniq -c | awk '{print $2"="$1}' | xargs
# ksh sc
A=11 C=19 G=2 M=1 N=8 R=1 S=1 T=2 Y=1
Length of XYZ.abc01=46
A=23 C=37 G=2 M=3 N=8 R=1 S=1 T=2 Y=3
Length of XYYUIZ.abc01=80
A=18 C=31 G=2 M=2 N=4 R=1 S=1 T=2 Y=2
Length of XYYU.abc03=63
A=17 C=26 G=2 M=2 N=3 R=1 S=1 T=2 Y=2
Length of XYYUIZ.abc04=56
A=16 C=24 G=2 M=2 N=6 R=1 S=1 T=2 Y=2
Length of XYYUIZ.abc05=56
A=22 C=32 G=2 M=3 N=8 R=1 S=1 T=2 Y=3
Length of XYYUIZ.abc06=74
A=16 C=24 G=2 M=2 N=3 R=1 S=1 T=2 Y=2
Length of XYYUIZ.abc07=53

Create an awk script awk_cmd

#!/bin/awk
{
        if($0~/^$/)
                next;
        if($0~/^>/)
        {
                label=$0;
                next;
        }
        for(x=1;x<=length($0);x++)
        {arr[substr($0,x,1)]++
        }
        for (i in arr)
        {
                print i":"arr;sum+=arr;arr=0
        }
        print "Length of "label"="sum;sum=0
}

Execute :

awk -f awk_cmd inputfile

Ruby(1.9+)

$ ruby -ne 'next if $.==1;$_.chomp.split(//).group_by{|x|x}.each{|x,y| puts "#{x}:#{y.count}"}' file
N:8
T:2
C:19
G:2
A:11
M:1
Y:1
S:1
R:1

@ctsgnb
Thank you !

@Bartus
Thanks a lot for the Perl version. Could you please explain the use of -F switch and // after that ?
Cheers :wink:

As you remember from previous Perl one-liners, -a is splitting each line into @F array, using space as default delimiter. To specify other delimiter we use -F option with regular expression following it (as with split function). If we use empty regular expression: // , each line will be split into separate characters, so each of the line's characters will be contained in separate @F field.

you can add

[[ -z $a ]] && continue

just after the "do" line so that empty lines will not be a problem