Правильный подсчет нужных строк
Модераторы: /dev/random, Модераторы разделов
-
Grih65kop
- Сообщения: 145
Правильный подсчет нужных строк
Имется текстовый файл следующего содержания:
asdc erqqerr 01.10.2009 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
4dss erqqerr 18.02.2010 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
f34gg erqqerr 26.04.2008 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
ds34d rrrrrr 14.09.2009 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
vcer2 rrrrrr 27.03.2008 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
qqw2 erqqerr 03.11.2007 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
bx2 erqqerr 08.09.2009 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
dfc dddddd 08.04.2010 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
1324 dddddd 02.04.2010 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
er35 erqqerr 24.02.2010 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
Нужно подсчитать сколько раз повторяется та или иная строка из второго столбика.
Придумал так:
grep -i "erqqerr" file.txt | wc -l
grep -i "rrrrrr" file.txt | wc -l
Т.е. выводим только нужные строки и подсчитаем их колличество, и так для каждого нужного значения.
Но это ужас каждый раз вводить отдельно rrrrrr, erqqerr, dddddd, etc. для получения одного результата. Можно ли как то улучшить жизнь скомпоновав это все в одно?
В идеале хотелось бы достичь подобного:
erqqerr: 551
rrrrrr: 123
dddddd: 100
asdc erqqerr 01.10.2009 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
4dss erqqerr 18.02.2010 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
f34gg erqqerr 26.04.2008 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
ds34d rrrrrr 14.09.2009 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
vcer2 rrrrrr 27.03.2008 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
qqw2 erqqerr 03.11.2007 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
bx2 erqqerr 08.09.2009 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
dfc dddddd 08.04.2010 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
1324 dddddd 02.04.2010 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
er35 erqqerr 24.02.2010 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
Нужно подсчитать сколько раз повторяется та или иная строка из второго столбика.
Придумал так:
grep -i "erqqerr" file.txt | wc -l
grep -i "rrrrrr" file.txt | wc -l
Т.е. выводим только нужные строки и подсчитаем их колличество, и так для каждого нужного значения.
Но это ужас каждый раз вводить отдельно rrrrrr, erqqerr, dddddd, etc. для получения одного результата. Можно ли как то улучшить жизнь скомпоновав это все в одно?
В идеале хотелось бы достичь подобного:
erqqerr: 551
rrrrrr: 123
dddddd: 100
-
watashiwa_daredeska
- Бывший модератор
- Сообщения: 4038
- Статус: Искусственный интеллект (pre-alpha)
- ОС: Debian GNU/Linux
Re: Правильный подсчет нужных строк
Код: Выделить всё
(cut -d' ' -f2 | sort | uniq -c) <file.txtМои розовые очки
Спасибо сказали:
-
Grih65kop
- Сообщения: 145
Re: Правильный подсчет нужных строк
Понял почему у меня не работал вариант от watashiwa_daredeska, у нас такой формат:
Форум резал эти пробелы. Оригинал в этом посте, видимо это какие то не обычные пробелы поэтому их не берет -d' '.
Код: Выделить всё
13d erqqerr 0w.01.2010 Постоянный никогда Постоянно [Снять бан]Форум резал эти пробелы. Оригинал в этом посте, видимо это какие то не обычные пробелы поэтому их не берет -d' '.
-
/dev/random
- Администратор
- Сообщения: 5458
- ОС: Gentoo
Re: Правильный подсчет нужных строк
Grih65kop писал(а): ↑15.04.2010 21:19Понял почему у меня не работал вариант от watashiwa_daredeska, у нас такой формат:
Код: Выделить всё
13d erqqerr 0w.01.2010 Постоянный никогда Постоянно [Снять бан]
Форум резал эти пробелы. Оригинал в этом посте, видимо это какие то не обычные пробелы поэтому их не берет -d' '.
Это табы. -d' ' можно просто убрать, cut по-умолчанию использует в качестве разделителя именно табы.
Спасибо сказали:
-
t.t
- Бывший модератор
- Сообщения: 7390
- Статус: думающий о вечном
- ОС: Debian, LMDE
Re: Правильный подсчет нужных строк
Зачем же порождать лишний shell?watashiwa_daredeska писал(а): ↑15.04.2010 20:44Код: Выделить всё
(cut -d' ' -f2 | sort | uniq -c) <file.txt
Код: Выделить всё
cut -f2 <file.txt | sort | uniq -c¡иɯʎdʞ ин ʞɐʞ 'ɐнɔɐdʞǝdu qнεиж
-
drBatty
- Сообщения: 8735
- Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
- ОС: Slackware-current
Re: Правильный подсчет нужных строк
есть пробел, табуляция, перевод строки, возврат каретки (это пробельные)
а ещё есть и \xA0
-
SLEDopit
- Модератор
- Сообщения: 4824
- Статус: фанат консоли (=
- ОС: GNU/Debian, RHEL
Re: Правильный подсчет нужных строк
в таких случаях (когда не совсем понятно, что выступает в качестве разделителя) я бы пользовался оком. ему все равно пробелы, табуляция или их смесь выступает в качестве разделителя:
я бы сделал так:
Код: Выделить всё
$ echo -e "one two\t\tthree" | awk '{print $1"_"$2"_"$3}'
one_two_threeя бы сделал так:
Код: Выделить всё
awk '{print $2}' file.txt | sort | uniq -cUNIX is basically a simple operating system, but you have to be a genius to understand the simplicity. © Dennis Ritchie
The more you believe you don't do mistakes, the more bugs are in your code.
The more you believe you don't do mistakes, the more bugs are in your code.
Спасибо сказали:
-
drBatty
- Сообщения: 8735
- Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
- ОС: Slackware-current
-
t.t
- Бывший модератор
- Сообщения: 7390
- Статус: думающий о вечном
- ОС: Debian, LMDE
Re: Правильный подсчет нужных строк
Интересно, что будет быстрее: awk '{print $2}' или sed -r 's/^\S+\s+(\S+).*/\1/'? Наверное, всё-таки awk?
¡иɯʎdʞ ин ʞɐʞ 'ɐнɔɐdʞǝdu qнεиж
-
drBatty
- Сообщения: 8735
- Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
- ОС: Slackware-current
Re: Правильный подсчет нужных строк
конечно awk. искать второе поле в разы проще и быстрее, чем "непробел более 0 раз, затем пробел не менее одного раза, затем непробел более 0 раз" в каждой строке. Да ещё и потом в этих строках двигать вторые непробелы вправо, и завершать их нулём (awk может просто их выплюнуть в пайп, а sed приходится редактировать, а потом выплёвывать).
-
drBatty
- Сообщения: 8735
- Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
- ОС: Slackware-current
Re: Правильный подсчет нужных строк
можно было-бы и одной sort всё сделать, но sort не понимает -c (точнее понимает не так, как uniq -c), причём вторая uniq тут тоже не поможет - uniq не понимает поля (умеет только пропускать первые N полей). В итоге, и uniq и sort недоделанные, потому приходится лепить их вместе, и плюс ещё какой-нибудь костыль :(
-
drBatty
- Сообщения: 8735
- Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
- ОС: Slackware-current
Re: Правильный подсчет нужных строк
вот ещё хитрый вариант, он (возможно) быстрее, и обладает большими возможностями:
Ускорение связано с отсутствием сортировки - она реализуется не скриптом на перле (sort), а самой файловой системой.
Shell
$ sed -r 'h;s/\S+\s+(\S+).*/\1/;G;s/(.*)\n(.*)/echo "\2" >>\1.users/pe' f.txt
echo "asdc erqqerr 01.10.2009 недоступно недоступно Постоянный никогда Постоянно [Снять бан]" >>erqqerr.users
echo "4dss erqqerr 18.02.2010 недоступно недоступно Постоянный никогда Постоянно [Снять бан]" >>erqqerr.users
echo "f34gg erqqerr 26.04.2008 недоступно недоступно Постоянный никогда Постоянно [Снять бан]" >>erqqerr.users
echo "ds34d rrrrrr 14.09.2009 недоступно недоступно Постоянный никогда Постоянно [Снять бан]" >>rrrrrr.users
echo "vcer2 rrrrrr 27.03.2008 недоступно недоступно Постоянный никогда Постоянно [Снять бан]" >>rrrrrr.users
echo "qqw2 erqqerr 03.11.2007 недоступно недоступно Постоянный никогда Постоянно [Снять бан]" >>erqqerr.users
echo "bx2 erqqerr 08.09.2009 недоступно недоступно Постоянный никогда Постоянно [Снять бан]" >>erqqerr.users
echo "dfc dddddd 08.04.2010 недоступно недоступно Постоянный никогда Постоянно [Снять бан]" >>dddddd.users
echo "1324 dddddd 02.04.2010 недоступно недоступно Постоянный никогда Постоянно [Снять бан]" >>dddddd.users
echo "er35 erqqerr 24.02.2010 недоступно недоступно Постоянный никогда Постоянно [Снять бан]" >>erqqerr.users
$ wc -l *.users
2 dddddd.users
6 erqqerr.users
2 rrrrrr.users
10 итого
Ускорение связано с отсутствием сортировки - она реализуется не скриптом на перле (sort), а самой файловой системой.
-
Luinnar
- Сообщения: 246
- ОС: Solaris, Debian, Ubuntu
Re: Правильный подсчет нужных строк
Эх, в который раз убеждаюсь, что awk хорош. 
Вот и весь скрипт на awk для решения задачи:
За один проход, без сортировки. Быстрее вроде бы некуда.
А вот проверка:
Вот и весь скрипт на awk для решения задачи:
Код: Выделить всё
cat data.txt | awk '{++arr[$2]} END{for(i in arr) {print i ":" arr[i]}}'За один проход, без сортировки. Быстрее вроде бы некуда.
А вот проверка:
Код: Выделить всё
user@host /tmp # cat data.txt
asdc erqqerr 01.10.2009 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
4dss erqqerr 18.02.2010 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
f34gg erqqerr 26.04.2008 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
ds34d rrrrrr 14.09.2009 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
vcer2 rrrrrr 27.03.2008 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
qqw2 erqqerr 03.11.2007 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
bx2 erqqerr 08.09.2009 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
dfc dddddd 08.04.2010 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
1324 dddddd 02.04.2010 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
er35 erqqerr 24.02.2010 недоступно недоступно Постоянный никогда Постоянно [Снять бан]
user@host /tmp # cat data.txt | awk '{++arr[$2]} END{for(i in arr) {print i ":" arr[i]}}'
dddddd:2
erqqerr:6
rrrrrr:2-
drBatty
- Сообщения: 8735
- Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
- ОС: Slackware-current
Re: Правильный подсчет нужных строк
такое допустимо в любом ЯП с поддержкой ассоциативных массивов. Как я выше показал, подойдёт даже sed. А сортировка тут всё равно есть - она в самом ЯП, в этом массиве. И что быстрее - зависит от реализации ЯП, и от данных.
-
Luinnar
- Сообщения: 246
- ОС: Solaris, Debian, Ubuntu
Re: Правильный подсчет нужных строк
Понятно, что в любом, где есть ассоц. массивы, тут уж что кому удобнее.
А вот про сортировку, я бы не был так уверен. Вполне возможно, что ассоц. масствы в awk с помощью хэш таблиц реализованы, т.к. они в данном случае эффективнее сортировки будут.
-
drBatty
- Сообщения: 8735
- Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
- ОС: Slackware-current
Re: Правильный подсчет нужных строк
хеш таблица это тоже "сортировка". строки с одним ключом сортируются по хешам (у них один хеш), т.о., в вашей awk получается массив из трёх элементов, отсортированный по хешам.
-
Luinnar
- Сообщения: 246
- ОС: Solaris, Debian, Ubuntu
Re: Правильный подсчет нужных строк
Всё же не стоит смешивать классическую сортировку и хэш таблицы.
Операция поиска элемента в отсортированном массиве имеет трудоёмкость O(ln(n)), в то время как для хэш-таблиц трудоёмкость равна O(1). Вот вам и разница.
-
Nazyvaemykh
- Сообщения: 438
- Статус: Подопытный участник
Re: Правильный подсчет нужных строк
Не могу согласиться с этим утверждением.
¡ Страсть к разрушению есть творческая страсть!
-
drBatty
- Сообщения: 8735
- Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
- ОС: Slackware-current
Re: Правильный подсчет нужных строк
ну и что? я это не первый год знаю.
и как в вашем awk реализована сортировка по алфавиту?
и какую сложность имеет пересортировка хештаблицы в алфавитную?
-
sash-kan
- Администратор
- Сообщения: 13939
- Статус: oel ngati kameie
- ОС: GNU
Re: Правильный подсчет нужных строк
awk будет быстрее в этом конкретном примере, да.drBatty писал(а): ↑16.04.2010 19:07
конечно awk. искать второе поле в разы проще и быстрее, чем "непробел более 0 раз, затем пробел не менее одного раза, затем непробел более 0 раз" в каждой строке. Да ещё и потом в этих строках двигать вторые непробелы вправо, и завершать их нулём (awk может просто их выплюнуть в пайп, а sed приходится редактировать, а потом выплёвывать).
но, мне думается, разница происходит скорее от использумого математического аппарата. в sed это нка, в awk — дка.
могу и ошибаться в значимости этой разницы (для этого конкретного случая), так как в математике не силён.
Писать безграмотно - значит посягать на время людей, к которым мы адресуемся, а потому совершенно недопустимо в правильно организованном обществе. © Щерба Л. В., 1957
при сбоях форума см.блог
при сбоях форума см.блог
-
Luinnar
- Сообщения: 246
- ОС: Solaris, Debian, Ubuntu
Re: Правильный подсчет нужных строк
Почему нет?
Вы мне льстите, awk не мой, не я его разработал.
и какую сложность имеет пересортировка хештаблицы в алфавитную?
А это тут причем? awk вовсе и не сортирует ассоциативный массив по алфавиту.
А вообще, зачем гадать да спорить? Всё можно решить опытом.
Взял относительно большой текстовый файл и оставил в нём только слова, причем каждое слово на новой строке:
Код: Выделить всё
cat /etc/bash_completion | tr -sc 'a-zA-Z0-9_' '\n' > ~/tmp/text.txtТеперь запускаем для этого файла подсчет частоты встречаемости слов используя в одном случае sort | uniq -c, а в другом случае awk с ассоциативным массивом. Каждую из этих двух комманд выполняем три раза для надежности результата. Вот хроника событий:
Код: Выделить всё
user@host:tmp$ wc text.txt
27036 27035 153636 text.txt
user@host:tmp$ head text.txt
mode
shell
script
sh
basic
offset
8
indent
tabs
user@host:tmp$ tail text.txt
dirnames
filenames
have
nospace
bashdefault
plusdirs
set
BASH_COMPLETION_ORIGINAL_V_VALUE
unset
BASH_COMPLETION_ORIGINAL_V_VALUE
user@host:tmp$ cat script1.sh
sort | uniq -c
user@host:tmp$ cat script2.sh
awk '{++arr[$1]} END{for(i in arr) {print arr[i], i}}'
user@host:tmp$ time ./script1.sh < text.txt > /dev/null
real 0m0.176s
user 0m0.148s
sys 0m0.016s
user@host:tmp$ time ./script1.sh < text.txt > /dev/null
real 0m0.184s
user 0m0.168s
sys 0m0.008s
user@host:tmp$ time ./script1.sh < text.txt > /dev/null
real 0m0.204s
user 0m0.188s
sys 0m0.008s
user@host:tmp$ time ./script2.sh < text.txt > /dev/null
real 0m0.081s
user 0m0.056s
sys 0m0.012s
user@host:tmp$ time ./script2.sh < text.txt > /dev/null
real 0m0.111s
user 0m0.084s
sys 0m0.008s
user@host:tmp$ time ./script2.sh < text.txt > /dev/null
real 0m0.106s
user 0m0.076s
sys 0m0.012sВидно, что у варианта с сортировкой время выполнения 0.18-0.2 с, в то время как у awk время выполнения 0.08-0.11 с.
То есть awk примерно в 2 раза быстрее.
-
drBatty
- Сообщения: 8735
- Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
- ОС: Slackware-current
Re: Правильный подсчет нужных строк
вот вы это мне и покажите. а то что-то в вашем примере массив отсортирован. совпадение?
а почему скрыли результаты в большом файле, и оставили только время? время - не является доказательство, кроме того, даже на вскидку должен быть выигрыш между O(N) и O(N*log(N)) вовсе не в 2 раза.
вот и приведите пример того, как awk НЕ сортирует. А лучше - сошлитесь на исходный текст, тогда вопросов не будет.
-
Luinnar
- Сообщения: 246
- ОС: Solaris, Debian, Ubuntu
Re: Правильный подсчет нужных строк
Откуда такое недоверие? Вот, пожалуйста:
Код: Выделить всё
user@host:tmp$ echo|awk 'END{a["b"]=3;a["c"]=1;a["z"]=2; for(i in a) print i, a[i]}'
z 2
b 3
c 1Могли бы и сами, кстати, проверить.
А вы хотели, чтобы я 4.5 тыс строк результата сюда выложил? :-D
Ладно, здесь нам head поможет:
Код: Выделить всё
user@host:tmp$ ./script1.sh < text.txt | head
1
5 _
628 0
1 02111
1 03
2 04
1 05
3 05b
453 1
1 10xCelsius
user@host:tmp$ ./script2.sh < text.txt | head
2 ra
1
18 rc
4 _services
13 limit
14 verify
7 re
2 recip
5 mutt
6 rm
user@host:tmp$ diff -w <(./script1.sh < text.txt) <(./script2.sh < text.txt|sort -k2) && echo EQUAL
EQUAL-
drBatty
- Сообщения: 8735
- Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
- ОС: Slackware-current
Re: Правильный подсчет нужных строк
недоверие потому, что у вас здесь: Правильный подсчет нужных строк
получился отсортированный массив. А сам я не собираюсь проверять потому, что
а) лично я не люблю awk, по той причине, что простые задачи проще всего решить на sed, а более сложные - на С++.
б) тут нужно стандарт читать, поведение awk должно быть описано, и это надо прочитать перед тем, как что-то делать. Мне - лень. Я лучше сам сделаю хештаблицу или массив на С++, чем буду разбираться, как это реализовано в awk.
-
drBatty
- Сообщения: 8735
- Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
- ОС: Slackware-current
Re: Правильный подсчет нужных строк
Luinnar кстати. посмотрел по ссылке - там у вас сортировка всё-же есть, просто вы её не видите. Потому и время такое странное (для массива из ~1000 элементов выигрыш должен составить ~10 раз, т.к. log(1000) ~ 10, а у вас - 2).
-
Luinnar
- Сообщения: 246
- ОС: Solaris, Debian, Ubuntu
Re: Правильный подсчет нужных строк
drBatty писал(а): ↑17.04.2010 12:54
недоверие потому, что у вас здесь: Правильный подсчет нужных строк
получился отсортированный массив. А сам я не собираюсь проверять потому, что
а) лично я не люблю awk, по той причине, что простые задачи проще всего решить на sed, а более сложные - на С++.
б) тут нужно стандарт читать, поведение awk должно быть описано, и это надо прочитать перед тем, как что-то делать. Мне - лень. Я лучше сам сделаю хештаблицу или массив на С++, чем буду разбираться, как это реализовано в awk.
Ну так это лично ВАША нелюбовь к awk. Вы же из-за этой нелюбви пытаетесь доказать, что awk плох, ну или не достаточно хорош, что, по-моему, совершенно лишнее. Есть разные средства, кто чем пользуется - личное дело каждого.
А то, что массив из трех элементов оказался отсортированным, в том конкретном случае было совпадением.
посмотрел по ссылке - там у вас сортировка всё-же есть, просто вы её не видите. Потому и время такое странное (для массива из ~1000 элементов выигрыш должен составить ~10 раз, т.к. log(1000) ~ 10, а у вас - 2).
И где же конкретно вы узрели отсортированные данные?