Saya memiliki file MetaData.csv yang berisi banyak nilai untuk melakukan analisis. Yang saya inginkan adalah: 1- Membaca nama kolom dan membuat variabel yang mirip dengan nama kolom. 2- Masukkan nilai di setiap kolom ke dalam variabel sebagai bilangan bulat yang dapat dibaca oleh perintah lain. column_name=Nilai_nya

MetaData.csv:

MAF,HWE,Geno_Missing,Inds_Missing
0.05,1E-06,0.01,0.01

Saya menulis kode berikut tetapi tidak berfungsi dengan baik:

#!/bin/bash
Col_Names=$(head -n 1 MetaData.csv) # Cut header (camma sep)
Col_Names=$(echo ${Col_Names//,/ }) # Convert header to space sep
Col_Names=($Col_Names) # Convert header to an array 

for i in $(seq 1 ${#Col_Names[@]}); do
N="$(head -1 MetaData.csv | tr ',' '\n' | nl |grep -w 
"${Col_Names[$i]}" | tr -d " " | awk -F " " '{print $1}')";
${Col_Names[$i]}="$(cat MetaData.csv | cut -d"," -f$N | sed '1d')";
done

Keluaran:

HWE=1E-06: command not found
Geno_Missing=0.01: command not found
Inds_Missing=0.01: command not found
cut: 2: No such file or directory
cut: 3: No such file or directory
cut: 4: No such file or directory
=: command not found

Keluaran yang diharapkan:

MAF=0.05
HWE=1E-06
Geno_Missing=0.01
Inds_Missing=0.01

Masalah:

1- Saya ingin menggunakan panjang array (${#Col_Names[@]}) sebagai iterasi terakhir yaitu 5, tetapi indeks array mulai dari 0 (0-4). Jadi kolom MAF tidak ditangkap oleh loop. Loop juga mengulangi dua kali (sekali 0-4 dan lagi 2-4!). 2- Ketika saya mencoba memanggil nilai dalam variabel (echo $MAF), mereka kosong!

Solusi apa pun sangat dihargai.

-1
Mehdi Esmaeilifard 3 Januari 2021, 19:51

3 jawaban

Jawaban Terbaik

Jika saya memahami persyaratan Anda dengan benar, tolong coba sesuatu seperti:

#!/bin/bash

nr=1                                    # initialize input line number to 1
while IFS=, read -r -a ary; do          # split the line on "," then assign "ary" to the fields
    if (( nr == 1 )); then              # handle the header line
        col_names=("${ary[@]}")         # assign column names
    else                                # handle the body lines
        for (( i = 0; i < ${#ary[@]}; i++ )); do
            printf -v "${col_names[i]}" "${ary[i]}"
                                        # assign the variable "${col_names[i]}" to the input field
        done
        # now you can access the values via its column name
        echo "Fnames=$Fnames"
        echo "MAF=$MAF"
        fname_list+=("$Fnames")         # create a list of Fnames
    fi
    (( nr++ ))                          # increment the input line number
done < MetaData.csv
echo "${fname_list[@]}"                 # print the list of Fnames

Keluaran:

Fnames=19.vcf.gz
MAF=0.05
Fnames=20.vcf.gz
MAF=
Fnames=21.vcf.gz
MAF=
Fnames=22.vcf.gz
MAF=
19.vcf.gz 20.vcf.gz 21.vcf.gz 22.vcf.gz
  • Statetemt IFS=, read -a ary sebagian besar setara dengan Anda tiga baris pertama; itu membagi input pada ",", dan menetapkan variabel array ary ke nilai bidang.
  • Ada beberapa cara untuk menggunakan nilai variabel sebagai nama variabel (Referensi Variabel Tidak Langsung). printf -v VarName Value adalah salah satunya.

[EDIT]

Berdasarkan file input OP yang diperbarui, berikut adalah versi lain:

#!/bin/bash

nr=1                                    # initialize input line number to 1
while IFS=, read -r -a ary; do          # split the line on "," then assign "ary" to the fields
    if (( nr == 1 )); then              # handle the header line
        col_names=("${ary[@]}")         # assign column names
    else                                # handle the body lines
        for (( i = 0; i < ${#ary[@]}; i++ )); do
            printf -v "${col_names[i]}" "${ary[i]}"
                                        # assign the variable "${col_names[i]}" to the input field
        done
    fi
    (( nr++ ))                          # increment the input line number
done < MetaData.csv

for n in "${col_names[@]}"; do          # iterate over the variable names
    echo "$n=${!n}"                     # print variable name and its value
done

# you can also specify the variable names literally as follows:
echo "MAF=$MAF HWE=$HWE Geno_Missing=$Geno_Missing Inds_Missing=$Inds_Missing"

Keluaran:

MAF=0.05
HWE=1E-06
Geno_Missing=0.01
Inds_Missing=0.01
MAF=0.05 HWE=1E-06 Geno_Missing=0.01 Inds_Missing=0.01

Sedangkan untuk output, empat baris pertama dicetak oleh echo "$n=${!n}" dan baris terakhir dicetak oleh echo "MAF=$MAF .... Anda dapat memilih salah satu pernyataan tergantung pada penggunaan variabel dalam kode berikut.

0
tshiono 7 Januari 2021, 00:39

Ini menghasilkan output yang diharapkan yang Anda posting dari input sampel yang Anda posting:

$ awk -F, -v OFS='=' 'NR==1{split($0,hdr); next} {for (i=1;i<=NF;i++) print hdr[i], $i}' MetaData.csv
MAF=0.05
HWE=1E-06
Geno_Missing=0.01
Inds_Missing=0.01

Jika bukan hanya itu yang Anda butuhkan, edit pertanyaan Anda untuk memperjelas persyaratan Anda.

2
Ed Morton 6 Januari 2021, 16:02

Saya tidak benar-benar berpikir Anda dapat menerapkan pembaca/parser CSV yang kuat di Bash, tetapi Anda dapat mengimplementasikannya untuk bekerja sampai batas tertentu dengan file CSV sederhana. Misalnya, CSV yang diterapkan bash sangat sederhana mungkin terlihat seperti ini:

#!/bin/bash

set -e

ROW_NUMBER='0'
HEADERS=()
while IFS=',' read -ra ROW; do
    if test "$ROW_NUMBER" == '0'; then
        for (( I = 0; I < ${#ROW[@]}; I++ )); do
            HEADERS["$I"]="${ROW[I]}"
        done
    else
        declare -A DATA_ROW_MAP
        for (( I = 0; I < ${#ROW[@]}; I++ )); do
            DATA_ROW_MAP[${HEADERS["$I"]}]="${ROW[I]}"
        done
# DEMO {
        echo -e "${DATA_ROW_MAP['Fnames']}\t${DATA_ROW_MAP['Inds_Missing']}"
# } DEMO
        unset DATA_ROW_MAP
    fi
    ROW_NUMBER=$((ROW_NUMBER + 1))
done

Perhatikan bahwa ini memiliki beberapa kelemahan:

  • ini hanya berfungsi dengan bidang yang dipisahkan , (benar-benar "C"SV);
  • itu tidak dapat menangani catatan multiline;
  • itu tidak bisa menangani pelarian lapangan;
  • itu menganggap baris pertama selalu mewakili baris header.

Inilah sebabnya mengapa banyak perintah dapat menghasilkan dan menggunakan data yang dibatasi \0 hanya karena karakter kontrol ini mungkin lebih mudah digunakan. Sekarang yang saya tidak yakin adalah apakah test adalah satu-satunya perintah eksternal yang dieksekusi oleh bash (saya yakin demikian, tetapi mungkin dapat diimplementasikan kembali menggunakan case sehingga tidak eksternal test dieksekusi?).

Contoh penggunaan (dengan output demo):

./read-csv.sh < MetaData.csv
19.vcf.gz    0.01
20.vcf.gz
21.vcf.gz
22.vcf.gz

Saya tidak akan merekomendasikan menggunakan parser ini sama sekali, tetapi akan merekomendasikan menggunakan alat yang lebih berorientasi CSV (Python mungkin akan menjadi pilihan termudah untuk digunakan; + atau jika bahasa favorit Anda, seperti yang Anda sebutkan, adalah R, maka mungkin ini opsi lain untuk Anda: Jalankan skrip R dari baris perintah ).

0
fluffy 4 Januari 2021, 14:37