如何将每个列值分配给它的名称?
How can I assign each column value to Its name?
我有一个 MetaData.csv 文件,其中包含许多用于执行分析的值。我想要的是:
1-读取列名并使变量类似于列名。
2- 将每列中的值作为整数放入变量中,以供其他命令读取。 column_name=Its_value
MetaData.csv:
MAF,HWE,Geno_Missing,Inds_Missing
0.05,1E-06,0.01,0.01
我写了以下代码,但效果不佳:
#!/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 }')";
${Col_Names[$i]}="$(cat MetaData.csv | cut -d"," -f$N | sed '1d')";
done
输出:
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
预期输出:
MAF=0.05
HWE=1E-06
Geno_Missing=0.01
Inds_Missing=0.01
问题:
1- 我想使用数组长度 (${#Col_Names[@]}) 作为最终迭代,即 5,但数组索引从 0 (0-4) 开始。因此 MAF 列未被循环捕获。循环也迭代两次(一次 0-4 和一次 2-4!)。
2- 当我尝试调用变量中的值时 (echo $MAF),它们是空的!
非常感谢任何解决方案。
我真的不认为您可以在 Bash 中实现强大的 CSV reader/parser,但您可以实现它以在某种程度上使用简单的 CSV 文件。例如,一个非常简单的 bash
实现的 CSV 可能如下所示:
#!/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
请注意,它有多个缺点:
- 它仅适用于
,
分隔的字段(真正的“C”SV);
- 无法处理多行记录;
- 它无法处理字段转义;
- 它认为第一行始终代表 header 行。
这就是为什么许多命令可能会生成和使用 [=15=]
分隔数据的原因,因为此控制字符可能更易于使用。现在我不确定 test
是否是 bash
执行的唯一外部命令(我相信是,但它可能是 re-implemented 使用 case
所以没有外部 test
被执行?)。
使用示例(带有演示输出):
./read-csv.sh < MetaData.csv
19.vcf.gz 0.01
20.vcf.gz
21.vcf.gz
22.vcf.gz
我根本不推荐使用这个解析器,但会推荐使用更多 CSV-oriented 工具(Python 可能是最容易使用的选择;+ 或者如果您喜欢的语言,正如你提到的,是 R,那么这可能是你的另一个选择:Run R script from command line)。
如果我没有正确理解您的要求,请您尝试这样的操作:
#!/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
输出:
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
基本上等同于你的
前三行;它将输入拆分为“,”,并分配给
数组变量 ary
到字段值。
- 有几种方法可以使用变量的值作为变量名
(间接变量引用)。
printf -v VarName Value
就是其中之一。
[编辑]
根据 OP 更新的输入文件,这里是另一个版本:
#!/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"
输出:
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
至于输出,前四行由echo "$n=${!n}"
打印,最后一行由echo "MAF=$MAF ...
打印。
您可以根据您在以下代码中对变量的使用情况选择任一语句。
这会根据您发布的示例输入生成您发布的预期输出:
$ awk -F, -v OFS='=' 'NR==1{split([=10=],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
如果这不是您所需要的全部,请编辑您的问题以阐明您的要求。
我有一个 MetaData.csv 文件,其中包含许多用于执行分析的值。我想要的是: 1-读取列名并使变量类似于列名。 2- 将每列中的值作为整数放入变量中,以供其他命令读取。 column_name=Its_value
MetaData.csv:
MAF,HWE,Geno_Missing,Inds_Missing
0.05,1E-06,0.01,0.01
我写了以下代码,但效果不佳:
#!/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 }')";
${Col_Names[$i]}="$(cat MetaData.csv | cut -d"," -f$N | sed '1d')";
done
输出:
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
预期输出:
MAF=0.05
HWE=1E-06
Geno_Missing=0.01
Inds_Missing=0.01
问题:
1- 我想使用数组长度 (${#Col_Names[@]}) 作为最终迭代,即 5,但数组索引从 0 (0-4) 开始。因此 MAF 列未被循环捕获。循环也迭代两次(一次 0-4 和一次 2-4!)。 2- 当我尝试调用变量中的值时 (echo $MAF),它们是空的!
非常感谢任何解决方案。
我真的不认为您可以在 Bash 中实现强大的 CSV reader/parser,但您可以实现它以在某种程度上使用简单的 CSV 文件。例如,一个非常简单的 bash
实现的 CSV 可能如下所示:
#!/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
请注意,它有多个缺点:
- 它仅适用于
,
分隔的字段(真正的“C”SV); - 无法处理多行记录;
- 它无法处理字段转义;
- 它认为第一行始终代表 header 行。
这就是为什么许多命令可能会生成和使用 [=15=]
分隔数据的原因,因为此控制字符可能更易于使用。现在我不确定 test
是否是 bash
执行的唯一外部命令(我相信是,但它可能是 re-implemented 使用 case
所以没有外部 test
被执行?)。
使用示例(带有演示输出):
./read-csv.sh < MetaData.csv
19.vcf.gz 0.01
20.vcf.gz
21.vcf.gz
22.vcf.gz
我根本不推荐使用这个解析器,但会推荐使用更多 CSV-oriented 工具(Python 可能是最容易使用的选择;+ 或者如果您喜欢的语言,正如你提到的,是 R,那么这可能是你的另一个选择:Run R script from command line)。
如果我没有正确理解您的要求,请您尝试这样的操作:
#!/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
输出:
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
基本上等同于你的 前三行;它将输入拆分为“,”,并分配给 数组变量ary
到字段值。 - 有几种方法可以使用变量的值作为变量名
(间接变量引用)。
printf -v VarName Value
就是其中之一。
[编辑]
根据 OP 更新的输入文件,这里是另一个版本:
#!/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"
输出:
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
至于输出,前四行由echo "$n=${!n}"
打印,最后一行由echo "MAF=$MAF ...
打印。
您可以根据您在以下代码中对变量的使用情况选择任一语句。
这会根据您发布的示例输入生成您发布的预期输出:
$ awk -F, -v OFS='=' 'NR==1{split([=10=],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
如果这不是您所需要的全部,请编辑您的问题以阐明您的要求。