Как бы я перебрать пары значений без повторения в Bash? - PullRequest
0 голосов
/ 06 июля 2019

Я использую конкретную программу, которая потребовала бы от меня изучения пар переменных в текстовом файле, указав пары с помощью индексов.

Например:

gcta  --reml-bivar 1 2 --grm test  --pheno test.phen  --out test

Где 1и 2 будет соответствовать значениям из первых двух столбцов в текстовом файле.Если бы у меня было 50 столбцов, и я хотел бы исследовать каждую пару без повторений (1 и 2, 2 и 3, 1 и 3 ... 50), каков был бы лучший способ автоматизировать это, пройдя через это?Таким образом, по сути сценарий будет выполнять ту же команду, но принимать пары индексов, например:

gcta  --reml-bivar 1 3 --grm test  --pheno test.phen  --out test
gcta  --reml-bivar 1 4 --grm test  --pheno test.phen  --out test

... и так далее, и тому подобное.Спасибо!

Ответы [ 3 ]

0 голосов
/ 06 июля 2019

Если я вас правильно понимаю, и вам не нужны пары, похожие на '1 1', '2 2', ... и '1 2', '2 1' ... попробуйте этот скрипт

#!/bin/bash

for i in $(seq 1 49);
do
    for j in $(seq $(($i + 1)) 50);
    do gcta --reml-bivar "$i $j" --grm test --pheno test.phen --out test
done;

done;
0 голосов
/ 06 июля 2019

Поскольку вы не показали нам ни одного примера ввода, мы просто предполагаем, но если ваш ввод представляет собой список чисел (извлеченный из файла или иным образом), то вот подход:

$ cat combinations.awk
###################
# Calculate all combinations of a set of strings, see
# See https://rosettacode.org/wiki/Combinations#AWK
###################

function get_combs(A,B, i,n,comb) {
    ## Default value for r is to choose 2 from pool of all elements in A.
    ## Can alternatively be set on the command line:-
    ##    awk -v r=<number of items being chosen> -f <scriptname>
    n = length(A)
    if (r=="") r = 2

    comb = ""
    for (i=1; i <= r; i++) { ## First combination of items:
        indices[i] = i
        comb = (i>1 ? comb OFS : "") A[indices[i]]
    }
    B[comb]

    ## While 1st item is less than its maximum permitted value...
    while (indices[1] < n - r + 1) {
        ## loop backwards through all items in the previous
        ## combination of items until an item is found that is
        ## less than its maximum permitted value:
        for (i = r; i >= 1; i--) {
            ## If the equivalently positioned item in the
            ## previous combination of items is less than its
            ## maximum permitted value...
            if (indices[i] < n - r + i) {
                ## increment the current item by 1:
                indices[i]++
                ## Save the current position-index for use
                ## outside this "for" loop:
                p = i
                break}}
        ## Put consecutive numbers in the remainder of the array,
        ## counting up from position-index p.
        for (i = p + 1; i <= r; i++) indices[i] = indices[i - 1] + 1

        ## Print the current combination of items:
        comb = ""
        for (i=1; i <= r; i++) {
            comb = (i>1 ? comb OFS : "") A[indices[i]]
        }
        B[comb]
    }
}

# Input should be a list of strings
{
    split($0,A)
    delete B
    get_combs(A,B)
    PROCINFO["sorted_in"] = "@ind_str_asc"
    for (comb in B) {
        print comb
    }
}

.

$ awk -f combinations.awk <<< '1 2 3 4'
1 2
1 3
1 4
2 3
2 4
3 4

.

$ while read -r a b; do
    echo gcta  --reml-bivar "$a" "$b" --grm test  --pheno test.phen  --out test
done < <(awk -f combinations.awk <<< '1 2 3 4')
gcta --reml-bivar 1 2 --grm test --pheno test.phen --out test
gcta --reml-bivar 1 3 --grm test --pheno test.phen --out test
gcta --reml-bivar 1 4 --grm test --pheno test.phen --out test
gcta --reml-bivar 2 3 --grm test --pheno test.phen --out test
gcta --reml-bivar 2 4 --grm test --pheno test.phen --out test
gcta --reml-bivar 3 4 --grm test --pheno test.phen --out test

Удалите echo, когда вы закончите тестирование и будете довольны выводом.

В случае, если кто-то читает это и вместо этого хочет перестановкикомбинаций:

$ cat permutations.awk
###################
# Calculate all permutations of a set of strings, see
# https://en.wikipedia.org/wiki/Heap%27s_algorithm

function get_perm(A,            i, lgth, sep, str) {
    lgth = length(A)
    for (i=1; i<=lgth; i++) {
        str = str sep A[i]
        sep = " "
    }
    return str
}

function swap(A, x, y,  tmp) {
    tmp  = A[x]
    A[x] = A[y]
    A[y] = tmp
}

function generate(n, A, B,      i) {
    if (n == 1) {
        B[get_perm(A)]
    }
    else {
        for (i=1; i <= n; i++) {
            generate(n - 1, A, B)
            if ((n%2) == 0) {
                swap(A, 1, n)
            }
            else {
                swap(A, i, n)
            }
        }
    }
}

function get_perms(A,B) {
    generate(length(A), A, B)
}

###################

# Input should be a list of strings
{
    split($0,A)
    delete B
    get_perms(A,B)
    PROCINFO["sorted_in"] = "@ind_str_asc"
    for (perm in B) {
        print perm
    }
}

.

$ awk -f permutations.awk <<< '1 2 3 4'
1 2 3 4
1 2 4 3
1 3 2 4
1 3 4 2
1 4 2 3
1 4 3 2
2 1 3 4
2 1 4 3
2 3 1 4
2 3 4 1
2 4 1 3
2 4 3 1
3 1 2 4
3 1 4 2
3 2 1 4
3 2 4 1
3 4 1 2
3 4 2 1
4 1 2 3
4 1 3 2
4 2 1 3
4 2 3 1
4 3 1 2
4 3 2 1

Оба из перечисленных выше используют GNU awk для sorted_in для сортировки вывода.Если у вас нет GNU awk, вы все равно можете использовать сценарии как есть, а если вам нужно отсортировать вывод, перенаправьте его на sort.

0 голосов
/ 06 июля 2019

1 и 2 будут соответствовать значениям из первых двух столбцов в текстовом файле.

каждая пара без повторений

Итак, давайте пройдемся по этому процессу:

  1. Мы повторяем первый столбец из файла, умноженный на длину файла
  2. Мы повторяем каждое значение (каждую строку) из второго столбца из файла, умноженного на длину файла
  3. Присоединяемся к повторяющимся столбцам -> у нас есть все комбинации
  4. Нам нужно отфильтровать «повторы», мы можем просто объединить файл с исходным файлом и отфильтровать повторяющиеся столбцы
  5. Итак, мы получаем каждую пару без повторений.
  6. Тогда мы просто читаем файл построчно.

Сценарий:

# create an input file cause you didn't provide any
cat << EOF > in.txt
1 a
2 b
3 c
4 d
EOF

# get file length
inlen=$(<in.txt wc -l)

# join the columns
paste -d' ' <(
  # repeat the first column inlen times
  # https://askubuntu.com/questions/521465/how-can-i-repeat-the-content-of-a-file-n-times
  seq "$inlen" |
  xargs -I{} cut -d' ' -f1 in.txt
) <(
  # repeat each line inlen times
  # https://unix.stackexchange.com/questions/81904/repeat-each-line-multiple-times
  awk -v IFS=' ' -v v="$inlen" '{for(i=0;i<v;i++)print $2}' in.txt
) |
# filter out repetitions - ie. filter original lines from the file
sort |
comm --output-delimiter='' -3 <(sort in.txt) - |
# read the file line by line
while read -r one two; do
  echo "$one" "$two"
done

выведет:

1 b
1 c
1 d
2 a
2 c
2 d
3 a
3 b
3 d
4 a
4 b
4 c
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...