从值字符串中获取中位数
Get median from string of values
我需要找到保存在字符串中的值的中位数。我必须在 bash 中实现此功能而无需任何其他临时文件,并且我 不能 使用 awk。
我把这个字符串保存在 $string
:
85 13 4 45 1111 89 87 66 1 5 2 51 13 66 98 50 20 14 18 16 31 21 5175 12
首先,我需要像这样对这些值进行排序:
1 2 4 5 12 13 13 14 16 18 20 21 31 45 50 51 66 66 85 87 89 98 1111 5175
然后我需要找到这些值的中位数
(21+31) / 2 = 26
我怎样才能做到这一点? bash有没有有效的方法或命令?
我的想法:
要对值进行排序,我可以使用 sort
,但我不确定如何强制它对字符串中的值进行排序,因为它使用的是 FILE。
虽然我不知道如何实现中位数,所以我希望至少能得到一些小提示。
您可以使用如下命令:
str="85 13 4 45 1111 89 87 66 1 5 2 51 13 66 98 50 20 14 18 16 31 21 5175 12"
count=$(echo $str | wc -w)
arr=($(echo $str | tr " " "\n" | sort -n ))
#echo ${arr[*]}
if [[ $(( $count % 2 )) == 0 ]]; then
# even element count, get the elements around the middle
f1=${arr[ $(( (count - 1 ) /2 )) ]}
f2=${arr[ $(( (count + 1 ) /2 )) ]}
#echo "f1=$f1, f2=$f2"
echo $(( ($f1 + f2) / 2 ))
else
# odd element count
echo ${arr[ $(( $count / 2 ))]}
fi
要将字符串中的数字放入排序数组中,您可以将它们分别打印在单独的一行上,通过管道传输到 sort -n
,然后使用 mapfile
:[=22 读入数组=]
string='85 13 4 45 1111 89 87 66 1 5 2 51 13 66 98 50 20 14 18 16 31 21 5175 12'
mapfile -t arr < <(for num in $string; do echo "$num"; done | sort -n)
-t
选项从每个值中删除换行符。请注意,您 不能 管道到 mapfile
因为那将在子外壳中并且 arr
之后将是空的。
引用你的变量通常是个好主意,但在这种情况下,我们依赖分词,不能引用 $string
。
现在,对于中位数,有两种选择:
- 数组元素个数为奇数,我们只需要中间元素的值。
- 数组元素个数为偶数,求中间两个元素的平均值
数组元素的数量是${#arr[@]}
,所以我们可以检查一下,然后决定做什么:
nel=${#arr[@]}
if (( nel % 2 == 1 )); then # Odd number of elements
val="${arr[ $((nel/2)) ]}"
else # Even number of elements
val="$(( ( arr[$((nel/2))] + arr[$((nel/2-1))] ) / 2 ))"
fi
printf "%d\n" "$val"
这依赖于整数运算:如果我们有奇数个元素,比如三个,则中位数的索引为 1 – 我们可以从三除以二得到整数。对于偶数个元素,比如四个,我们需要索引 1 和 2 处的元素,我们通过将四除以二得到较高索引并从中减去一以获得较低索引。
如果两个元素相加不是偶数,结果将向下舍入。如果这还不够好,我们可以检查数字是否为奇数并手动将 .5
添加到结果中,或者我们可以使用 bc
进行计算。考虑:
$ echo $(( 11/2 ))
5
$ bc <<< 'scale=1; 11/2'
5.5
我需要找到保存在字符串中的值的中位数。我必须在 bash 中实现此功能而无需任何其他临时文件,并且我 不能 使用 awk。
我把这个字符串保存在 $string
:
85 13 4 45 1111 89 87 66 1 5 2 51 13 66 98 50 20 14 18 16 31 21 5175 12
首先,我需要像这样对这些值进行排序:
1 2 4 5 12 13 13 14 16 18 20 21 31 45 50 51 66 66 85 87 89 98 1111 5175
然后我需要找到这些值的中位数
(21+31) / 2 = 26
我怎样才能做到这一点? bash有没有有效的方法或命令?
我的想法:
要对值进行排序,我可以使用 sort
,但我不确定如何强制它对字符串中的值进行排序,因为它使用的是 FILE。
虽然我不知道如何实现中位数,所以我希望至少能得到一些小提示。
您可以使用如下命令:
str="85 13 4 45 1111 89 87 66 1 5 2 51 13 66 98 50 20 14 18 16 31 21 5175 12"
count=$(echo $str | wc -w)
arr=($(echo $str | tr " " "\n" | sort -n ))
#echo ${arr[*]}
if [[ $(( $count % 2 )) == 0 ]]; then
# even element count, get the elements around the middle
f1=${arr[ $(( (count - 1 ) /2 )) ]}
f2=${arr[ $(( (count + 1 ) /2 )) ]}
#echo "f1=$f1, f2=$f2"
echo $(( ($f1 + f2) / 2 ))
else
# odd element count
echo ${arr[ $(( $count / 2 ))]}
fi
要将字符串中的数字放入排序数组中,您可以将它们分别打印在单独的一行上,通过管道传输到 sort -n
,然后使用 mapfile
:[=22 读入数组=]
string='85 13 4 45 1111 89 87 66 1 5 2 51 13 66 98 50 20 14 18 16 31 21 5175 12'
mapfile -t arr < <(for num in $string; do echo "$num"; done | sort -n)
-t
选项从每个值中删除换行符。请注意,您 不能 管道到 mapfile
因为那将在子外壳中并且 arr
之后将是空的。
引用你的变量通常是个好主意,但在这种情况下,我们依赖分词,不能引用 $string
。
现在,对于中位数,有两种选择:
- 数组元素个数为奇数,我们只需要中间元素的值。
- 数组元素个数为偶数,求中间两个元素的平均值
数组元素的数量是${#arr[@]}
,所以我们可以检查一下,然后决定做什么:
nel=${#arr[@]}
if (( nel % 2 == 1 )); then # Odd number of elements
val="${arr[ $((nel/2)) ]}"
else # Even number of elements
val="$(( ( arr[$((nel/2))] + arr[$((nel/2-1))] ) / 2 ))"
fi
printf "%d\n" "$val"
这依赖于整数运算:如果我们有奇数个元素,比如三个,则中位数的索引为 1 – 我们可以从三除以二得到整数。对于偶数个元素,比如四个,我们需要索引 1 和 2 处的元素,我们通过将四除以二得到较高索引并从中减去一以获得较低索引。
如果两个元素相加不是偶数,结果将向下舍入。如果这还不够好,我们可以检查数字是否为奇数并手动将 .5
添加到结果中,或者我们可以使用 bc
进行计算。考虑:
$ echo $(( 11/2 ))
5
$ bc <<< 'scale=1; 11/2'
5.5