在bash中创建直方图

时间:2016-09-21 10:49:20

标签: bash loops awk histogram

修改

我读到的问题是,这应该是(this one)的副本。我不同意。在该问题中,目标是获得列中各个数字的频率。但是,如果我将这个解决方案应用到我的问题中,我仍然面临着将特定范围内的数字频率分组到最终直方图中的初始问题。即如果该解决方案告诉我0.45的频率为20.441(对于我的输入数据),我还是留下了问题将这两个频率分组为范围3的总共0.4-0.5

结束编辑

问题 -

我有一长串数据,其值介于0和1之间。 这将是类型 -

0.34
0.45
0.44
0.12
0.45
0.98
.
.
.

允许重复的长列十进制值。

我试图将其更改为直方图类型的输出,例如(对于上面显示的输入) -

0.0-0.1  0
0.1-0.2  1
0.2-0.3  0
0.3-0.4  1 
0.4-0.5  3
0.5-0.6  0
0.6-0.7  0
0.7-0.8  0
0.8-0.9  0
0.9-1.0  1

基本上,第一列具有每个范围的下限和上限,第二列具有该范围内的条目数。

我写得很糟糕 -

for i in $(seq 0 0.1 0.9)
do 
    awk -v var=$i '{if ($1 > var && $1 < var+0.1 ) print $1}' input | wc -l; 
done

这基本上是在每个范围内找到的wc -l个条目。

输出格式不是问题的一部分。如果我只是得到与不同箱子相对应的频率,那就足够了。另请注意,bin大小应该是我建议的解决方案中的变量。

我已经阅读this回答,并希望避免循环。我确信awk绕过for循环的方式要快得多。你能帮帮我吗?

3 个答案:

答案 0 :(得分:2)

您将在此算法中找到的唯一循环是在文件的行周围。

这是一个如何实现你在bash中提出的问题的例子。可能bash不是最好的语言,因为数学运算速度慢。我使用bc,如果你愿意,你可以使用awk。

算法的工作原理

想象一下你有很多箱子:每个箱子对应一个间隔。每个箱子的特征在于宽度(CHANNEL_DIM)和位置。这些垃圾箱必须能够覆盖数据投放的整个时间间隔。执行number / bin_width的值,您就可以获得垃圾箱的位置。所以你只需要为该bin添加+1。 Here更详细的解释。

#!/bin/bash

# This is the input: you can use $1 and $2 to read input as cmd line argument
FILE='bash_hist_test.dat'
CHANNEL_NUMBER=9  # They are actually 10: 0 is already a channel

# check the max and the min to define the dimension of the channels:
MAX=`sort -n $FILE | tail -n 1`
MIN=`sort -rn $FILE | tail -n 1`

# Define the channel width 
CHANNEL_DIM_LONG=`echo "($MAX-$MIN)/($CHANNEL_NUMBER)" | bc -l` 
CHANNEL_DIM=`printf '%2.2f' $CHANNEL_DIM_LONG `
# Probably printf is not the best function in this context because
#+the result could be system dependent.

# Determine the channel for a given number
# Usage: find_channel <number_to_histogram> <width_of_histogram_channel>
function find_channel(){
  NUMBER=$1
  CHANNEL_DIM=$2

  # The channel is found dividing the value for the channel width and 
  #+rounding it.
  RESULT_LONG=`echo $NUMBER/$CHANNEL_DIM | bc -l`
  RESULT=`printf '%.0f' $RESULT_LONG`
  echo $RESULT
}

# Read the file and do the computuation
while IFS='' read -r line || [[ -n "$line" ]]; do

  CHANNEL=`find_channel $line $CHANNEL_DIM`

  [[ -z HIST[$CHANNEL] ]] && HIST[$CHANNEL]=0
  let HIST[$CHANNEL]+=1
done < $FILE

counter=0
for i in ${HIST[*]}; do
  CHANNEL_START=`echo "$CHANNEL_DIM * $counter - .04" | bc -l`
  CHANNEL_END=`echo " $CHANNEL_DIM * $counter + .05" | bc`
  printf '%+2.1f : %2.1f => %i\n' $CHANNEL_START $CHANNEL_END $i
  let counter+=1
done

希望这会有所帮助。如果您有其他问题,请评论。

答案 1 :(得分:2)

对于这个特定问题,我会丢弃最后一位数字,然后计算排序数据的出现次数:

$('.Count').each(function () {
    $(this).prop('Counter',0).animate({
        Counter: $(this).text()
    }, {
        duration: 4000,
        easing: 'swing',
        step: function (now) {
            $(this).text(Math.ceil(now));
        }
    });
});

,它在指定的输入集上给出:

cut -b1-3 | sort | uniq -c

输出格式化可以通过此 2 0.1 1 0.3 3 0.4 1 0.9 命令进行管道来完成:

awk

答案 2 :(得分:2)

按照我之前回答的相同算法,我在awk中写了一个非常快的脚本(看图片)。 enter image description here

脚本如下:

#/usr/bin/awk -f

BEGIN{
    bin_width=0.1;

}
{
    bin=int(($1-0.0001)/bin_width);
    if( bin in hist){
        hist[bin]+=1
    }else{
        hist[bin]=1
    }
}
END{
    for (h in hist)
        printf " * > %2.2f  ->  %i \n", h*bin_width, hist[h]
}

bin_width是每个频道的宽度。要使用该脚本,只需将其复制到文件中,使其可执行(使用chmod +x <namefile>)并使用./<namefile> <name_of_data_file>运行。

相关问题