Преобразование подсчетов в относительные вероятности

Фон

Создайте лексикон вероятностей на основе CSV-файла слов и подсчетов. Это прелюдия к проблеме с сегментацией текста, а не домашнее задание.

Проблема

Дан файл CSV со следующими словами и числами:

aardvark,10
aardwolf,9
armadillo,9
platypus,5
zebra,1

Создайте файл с вероятностями относительно наибольшего числа в файле:

aardvark,1
aardwolf,0.9
armadillo,0.9
platypus,0.5
zebra,0.1

Где, например, aardvark,1 вычисляется как aardvark,10/10, а platypus,0.5 рассчитывается как platypus,5/10.

Вопрос

Каков наиболее эффективный способ реализации сценария оболочки для создания файла относительных вероятностей?

Ограничения

  • Ни слова, ни числа не идут ни в каком порядке.
  • Нет основных языков программирования (таких как Perl, Ruby, Python, Java, C, Fortran или Cobol).
  • Приветствуются стандартные инструменты Unix, такие как awk, sed или sort.
  • Все вероятности должны относиться к самой высокой вероятности в файле.
  • Слова уникальны, числа нет.
  • Подсчеты — натуральные числа.

Благодарю вас!


person Dave Jarvis    schedule 04.02.2011    source источник
comment
@Marshall: bc - это не bashcalc, это настольный калькулятор, а dc - настольный калькулятор.   -  person Dennis Williamson    schedule 07.02.2011
comment
@Dennis: Спасибо, извините за путаницу.   -  person marshall.ward    schedule 07.02.2011


Ответы (2)


Нет необходимости читать файл дважды:

awk 'BEGIN {OFS = FS = ","} {a[$1] = $2} $2 > max {max=$2} END {for (w in a) print w, a[w]/max}' inputfile

Если вам нужен вывод, отсортированный по слову:

awk ... | sort

or

awk 'BEGIN {OFS = FS = ","} {a[$1] = $2; ind[j++] = $1} $2 > max {max=$2} END {n = asort(ind); for (i=1; i<=n; i++) print ind[i], a[ind[i]]/max}' inputfile

Если вам нужен вывод, отсортированный по вероятности:

awk ... | sort -t, -k2,2n -k1,1
person Dennis Williamson    schedule 04.02.2011
comment
Не читать файл дважды, безусловно, является эффективным подходом. - person Dave Jarvis; 04.02.2011
comment
Если вы хотите отформатировать числа, вы можете заменить {print w, a[w]/max} на {printf %s,%.3f\n, w, a[w]/max} (чтобы сохранить 3 цифры в конце) - person marshall.ward; 07.02.2011

Это не является защитой от ошибок, но что-то вроде этого должно работать:

#!/bin/bash

INPUT=data.cvs
OUTPUT=tally.cvs
DIGITS=1

OLDIFS=$IFS
IFS=,

maxval=0  # Assuming all $val are positive

while read name val
do
    if (( val > maxval )); then maxval=$val; fi
done < $INPUT

# Make sure $OUTPUT doesn't exist

touch $OUTPUT

while read name val
do
    tally=`echo "scale=$DIGITS; result=$val/$maxval; if (0 <= result && result < 1) { print "0" }; print result" | bc`
    echo "$name,$tally" >> $OUTPUT
done < $INPUT

IFS=$OLDIFS

Заимствовано из этого вопроса и различных поисковых запросов.

person marshall.ward    schedule 04.02.2011
comment
Отличный ответ и хороший пример чтения файлов CSV в bash. Однако чистое awk решение, вероятно, более эффективно. Спасибо. - person Dave Jarvis; 04.02.2011
comment
@Dave: Определенный реквизит для куруми - person marshall.ward; 04.02.2011