Слияние файлов с данными экспоненциального представления в первом столбце и как использовать uniq

Два вопроса по использованию команды uniq, помогите пожалуйста.

Первый вопрос

Скажем, у меня есть два файла;

$ cat 1.dat
0.1 1.23
0.2 1.45
0.3 1.67

$ cat 2.dat
0.3 1.67
0.4 1.78
0.5 1.89

Используя cat 1.dat 2.dat | sort -n | uniq > 3.dat, я могу объединить два файла в один. результаты:

0.1 1.23
0.2 1.45
0.3 1.67
0.4 1.78
0.5 1.89

Но если у меня есть научная запись в файле 1.dat,

$ cat 1.dat
1e-1 1.23
0.2 1.45
0.3 1.67

результат будет:

0.2 1.45
0.3 1.67
0.4 1.78
0.5 1.89
1e-1 1.23

это не то, чего я хочу, как я могу дать uniq понять, что 1e-1 — это число, а не строка.

Второй вопрос

То же, что и выше, но на этот раз пусть первая строка второго файла 2.dat будет немного другой (от 0.3 1.67 до 0.3 1.57).

$ cat 2.dat
0.3 1.57
0.4 1.78
0.5 1.89

Тогда результат будет:

0.1 1.23
0.2 1.45
0.3 1.67
0.3 1.57
0.4 1.78
0.5 1.89

Мой вопрос в том, как я мог использовать uniq только на основе значения из первого файла и найти повторение только из первого столбца, чтобы результаты были такими же:

0.1 1.23
0.2 1.45
0.3 1.67
0.4 1.78
0.5 1.89

Спасибо

Более сложные тестовые случаи

$ cat 1.dat
1e-6 -1.23
0.2 -1.45
110.7 1.55
0.3 1.67e-3

person Daniel    schedule 14.02.2013    source источник


Ответы (3)


один awk (gnu awk) однострочник решает ваши две проблемы

  awk '{a[$1*1];b[$1*1]=$0}END{asorti(a);for(i=1;i<=length(a);i++)print b[a[i]];}' file2 file1

тест с данными: обратите внимание, я сделал файл1 несортированным и 1.57 в файле2, как вы хотели:

kent$  head *
==> file1 <==
0.3 1.67
0.2 1.45
1e-1 1.23

==> file2 <==
0.3 1.57
0.4 1.78
0.5 1.89

kent$  awk '{a[$1*1];b[$1*1]=$0}END{asorti(a);for(i=1;i<=length(a);i++)print b[a[i]];}' file2 file1
1e-1 1.23
0.2 1.45
0.3 1.67
0.4 1.78
0.5 1.89

изменить

отображать 0.1 вместо 1e-1:

kent$  awk '{a[$1*1];b[$1*1]=$2}END{asorti(a);for(i=1;i<=length(a);i++)print a[i],b[a[i]];}' file2 file1
0.1 1.23
0.2 1.45
0.3 1.67
0.4 1.78
0.5 1.89

изменить 2

для точности awk по умолчанию (OFMT) равен %.6g, вы можете изменить его. но если вы хотите отображать разную точность по строкам, нам придется немного пошутить:

(я добавил 1e-9 в файл1)

kent$  awk '{id=sprintf("%.9f",$1*1);sub(/0*$/,"",id);a[id];b[id]=$2}END{asorti(a);for(i=1;i<=length(a);i++)print a[i],b[a[i]];}'  file2 file1 
0.000000001 1.23
0.2 1.45
0.3 1.67
0.4 1.78
0.5 1.89

если вы хотите отображать одинаковую точность числа для всех строк:

kent$  awk '{id=sprintf("%.9f",$1*1);a[id];b[id]=$2}END{asorti(a);for(i=1;i<=length(a);i++)print a[i],b[a[i]];}'  file2 file1 
0.000000001 1.23
0.200000000 1.45
0.300000000 1.67
0.400000000 1.78
0.500000000 1.89
person Kent    schedule 14.02.2013
comment
Еще один вопрос, может ли awk преобразовать 1e-1 в 0.1 на выходе? Большое спасибо! Или я могу начать новую тему. - person Daniel; 15.02.2013
comment
@Daniel - да, проверьте этот удивительный ответ stackoverflow.com/a/11378022/297323 (вот почему я использовал python) - person Fredrik Pihl; 15.02.2013
comment
Я должен сказать, что эта one line работа очень полезна для меня, так как у меня есть множество файлов для слияния, а также я уже написал сценарии оболочки, поэтому я ценю решение Python здесь (я думаю, Python был бы более мощным, чем awk??), но я буду использовать это решение awk. - person Daniel; 15.02.2013
comment
@ Дэни, да, awk, конечно, может. проверьте мое редактирование в ответе. Кстати, я буду рад, если вы примете мой ответ. :D - person Kent; 15.02.2013
comment
@Kent Подождите, зачем менять 1e-1 на 1e-9, и ваше решение больше не работает? И нет ли способа вывести формат %.7f для всего первого столбца? - person Daniel; 15.02.2013
comment
Хорошо, я использую это, наконец, awk '{a[$1*10];b[$1*10]=$2}END{asorti(a);for(i=1;i<=length(a);i++)print a[i]/10,b[a[i]];}' 1.dat 2.dat - person Daniel; 15.02.2013
comment
К вашему сведению, я обнаружил, что это тоже работает: cat 1.dat 2.dat | sort -g -u | awk '{ printf "%.6f %s\n", $1, $2 }' - person Daniel; 15.02.2013

Только первая часть:

cat 1.dat 2.dat | sort -g -u

1e-1 1.23
0.2 1.45
0.3 1.67
0.4 1.78
0.5 1.89

man sort

  -g, --general-numeric-sort
          compare according to general numerical value

 -u, --unique
          with -c, check for strict ordering; without -c, output only the first of an equal run
person sotapme    schedule 14.02.2013
comment
К вашему сведению, я обнаружил, что это тоже работает: cat 1.dat 2.dat | sort -g -u | awk '{ printf "%.6f %s\n", $1, $2 }', изменение на cat 2.dat 1.dat | sort -g -u | awk '{ printf "%.6f %s\n", $1, $2 }' сохранит значение 2.dat. Так что большое спасибо! - person Daniel; 15.02.2013

Чтобы изменить научную запись на десятичную, я прибегнул к python

#!/usr/bin/env python

import sys
import glob

infiles = []

for a in sys.argv:
    infiles.extend(glob.glob(a))

for f in infiles[1:]:
    with open(f) as fd:
        for line in fd:
            data = map(float, line.strip().split())
            print data[0], data[1]

выход:

$ ./sn.py 1.dat 2.dat
0.1 1.23
0.2 1.45
0.3 1.67
0.3 1.67
0.4 1.78
0.5 1.89
person Fredrik Pihl    schedule 14.02.2013
comment
хороший, однако вы не сортируете. если входные файлы не отсортированы, вывод будет другим, верно? также часть uniq тоже не сделана.. - person Kent; 15.02.2013