X column1 column2 column3
row1 0 1 2
row2 3 4 5
row3 6 7 8
row4 9 10 11
行Perl脚本来执行此操作,但是执行起来应该比本地bash函数要慢)。所以输出看起来应该像
X row1 row2 row3 row4
column1 0 3 6 9
column2 1 4 7 10
column3 2 5 8 11
我想到了这样的解决方案
似乎不是最有效的解决方案。我在这篇文章中已经看到了vi的解决方案,但是它仍然过慢。有什么想法/建议/好主意吗? :-)
#1 楼
awk '
{
for (i=1; i<=NF; i++) {
a[NR,i] = $i
}
}
NF>p { p = NF }
END {
for(j=1; j<=p; j++) {
str=a[1,j]
for(i=2; i<=NR; i++){
str=str" "a[i,j];
}
print str
}
}' file
输出
$ more file
0 1 2
3 4 5
6 7 8
9 10 11
$ ./shell.sh
0 3 6 9
1 4 7 10
2 5 8 11
Jonathan在10000行文件上针对Perl解决方案的性能
$ head -5 file
1 0 1 2
2 3 4 5
3 6 7 8
4 9 10 11
1 0 1 2
$ wc -l < file
10000
$ time perl test.pl file >/dev/null
real 0m0.480s
user 0m0.442s
sys 0m0.026s
$ time awk -f test.awk file >/dev/null
real 0m0.382s
user 0m0.367s
sys 0m0.011s
$ time perl test.pl file >/dev/null
real 0m0.481s
user 0m0.431s
sys 0m0.022s
$ time awk -f test.awk file >/dev/null
real 0m0.390s
user 0m0.370s
sys 0m0.010s
Ed Morton的编辑(@ ghostdog74,如果您不同意,可以删除)。脚本正在执行。它还使用制表符作为OP最初要求的分隔符,因此它可以处理空字段,并且在这种特殊情况下,它会巧合地增加输出。
$ cat tst.awk
BEGIN { FS=OFS="\t" }
{
for (rowNr=1;rowNr<=NF;rowNr++) {
cell[rowNr,NR] = $rowNr
}
maxRows = (NF > maxRows ? NF : maxRows)
maxCols = NR
}
END {
for (rowNr=1;rowNr<=maxRows;rowNr++) {
for (colNr=1;colNr<=maxCols;colNr++) {
printf "%s%s", cell[rowNr,colNr], (colNr < maxCols ? OFS : ORS)
}
}
}
$ awk -f tst.awk file
X row1 row2 row3 row4
column1 0 3 6 9
column2 1 4 7 10
column3 2 5 8 11
以上解决方案可在任何awk中使用(当然,旧的破损awk除外-存在YMMV)。为此,可以这样做:而不是将整个文件读入内存的版本。它还假定每行的字段数相同,并且对
ENDFILE
和ARGIND
使用GNU awk,但是任何awk都可以对FNR==1
和END
进行测试。评论
现在也可以处理行和列标签吗?
–乔纳森·莱弗勒(Jonathan Leffler)
09年11月13日在15:54
好-您是正确的;您的示例数据与问题的示例数据不匹配,但是您的代码在问题的示例数据上运行良好,并提供了所需的输出(给出或取空白与制表符间距)。主要是我的错误。
–乔纳森·莱弗勒(Jonathan Leffler)
09年11月13日在17:20
有趣的时机-我同意您会在awk中看到性能提升。我使用的是MacOS X 10.5.8,不使用“ gawk”;我使用的是Perl 5.10.1(32位版本)。我收集到您的数据是10000行,每行4列?无论如何,这并不重要。 awk和perl都是可行的解决方案(而awk解决方案则更整洁-我的Perl中的“定义”检查对于严格/警告下的免费运行是必需的),而且都不是懈怠,而且两者都可能比原始方法快外壳脚本解决方案。
–乔纳森·莱弗勒(Jonathan Leffler)
09年11月16日在9:43
在我最初的2.2GB矩阵上,perl解决方案比awk略快-350.103s与我使用perl 5.8.8 64bit的369.410s
– Federico Giorgi
09年11月16日在10:18
@ zx8754最大字段数仅适用于旧的非POSIX awk。可能不幸的是被命名为“ nawk”。它不适用于gawk或其他现代awks。
–埃德·莫顿(Ed Morton)
16年4月10日在14:50
#2 楼
rsrs
带有BSD和macOS,但可以从其他平台上的程序包管理器中获得。它以APL中的“重塑”功能命名。使用空格和制表符序列作为列分隔符:
rs -T
使用制表符作为列分隔符:
rs -c -C -T
将逗号用作列分隔符:
rs -c, -C, -T
-c
更改输入列分隔符,而-C
更改输出列分隔符。仅-c
或-C
会将分隔符设置为制表符。 -T
转置行和列。不要使用
-t
而不是-T
,因为它使用自动选择的列数通常是不正确的,因为选择了列数以使输出行填充显示的宽度(默认情况下为80个字符,但可以使用-w
进行更改。)一个警告是,当使用-C
指定输出列分隔符时,会在每行的末尾添加一个额外的列分隔符,但是可以使用诸如sed 's/.$//'
之类的字符删除多余的字符:最后一个或多个空列,因为列数是根据第一行的列数确定的: $ seq 4|paste -d, - -|rs -c, -C, -T
1,3,
2,4,
$ seq 4|paste -d, - -|rs -c, -C, -T|sed 's/.$//'
1,3
2,4
Ruby
$ rs -C, -c, -T<<<$'1,\n3,4'
1,3,4,
$ ruby -e'puts readlines.map{|x|x.chomp.split(",",-1)}.transpose.map{|x|x*","}'<<<$'1,\n3,4'
1,3
,4
的-1
参数不会在末尾丢弃空字段: split
函数形式:
$ ruby -e'p"a,,".split(",")'
["a"]
$ ruby -e'p"a,,".split(",",-1)'
["a", "", ""]
jq
$ tp(){ ruby -e'puts STDIN.read.split("\n").map{|x|x.split(ARGV[0],-1)}.transpose.map{|x|x*ARGV[0]}' -- "${1-$'\t'}";} $ seq 4|paste - -|tp|sed -n l 1\t3$ 2\t4$
pre >jq -R .|jq -sr 'map(./"\t")|transpose|map(join("\t"))[]'
将每条输入行打印为JSON字符串文字,jq -R .
(-s
)在将每一行解析为JSON之后为输入行创建一个数组,而--slurp
(-r
)输出字符串的内容而不是JSON字符串文字。--raw-output
运算符已重载以分割字符串。
函数形式:
/
评论
我对rs不熟悉-感谢您的指导! (链接指向Debian;上游似乎是mirbsd.org/MirOS/dist/mir/rs)
–tripleee
2015年11月26日13:00
@lalebarde至少在OS X附带的rs的实现中,-c单独将输入列分隔符设置为选项卡。
– nisetama
16 Mar 5 '16 at 12:20
@lalebarde,尝试使用bash的ANSI-C引号获取制表符:$'\ t'
–格伦·杰克曼
16年4月10日在11:51
这是一个极端的情况,但是对于具有很多行(如TTC TTA TTC TTC TTC TTT)的非常大的文件,运行rs -c''-C''-Tcols.seq会得到rs:没有内存:无法分配记忆。这是一个运行FreeBSD 11.0-RELEASE且系统内存为32 GB的系统。因此,我的猜测是rs将所有内容都放入RAM,这对于提高速度很有好处,但对大数据却不利。
– jrm
17年7月6日在9:35
jq在766MB的文件上使用了21Gb的ram。 40分钟后我将其杀死,但没有任何输出。
–Glubbdrubb
18年3月20日在9:50
#3 楼
Python解决方案:python -c "import sys; print('\n'.join(' '.join(c) for c in zip(*(l.split() for l in sys.stdin.readlines() if l.strip()))))" < input > output
以上内容基于以下内容:每行具有相同的列数(不执行填充)。
评论
这里有一个小问题:将l.split()替换为l.strip()。split()(Python 2.7),否则输出的最后一行会残缺。适用于任意列分隔符,如果分隔符存储在变量sep中,则使用l.strip()。split(sep)和sep.join(c)。
– krlmlr
2012年10月2日,下午4:18
#4 楼
sourceforge上的转置项目就是一个类似于coreutil的C程序。gcc transpose.c -o transpose
./transpose -t input > output #works with stdin, too.
评论
感谢您的链接。但是,在处理大型矩阵/文件时,它需要太多内存。
–tommy.carstensen
13年4月8日在9:41
它具有用于块大小和字段大小的参数:尝试调整-b和-f参数。
–飞羊
13年4月8日在14:54
默认块大小(--block或-b)为10kb,默认字段大小(--fieldmax或-f)为64,所以不能这样。我试过了。 (还是)感谢你的建议。
–tommy.carstensen
13年4月10日在16:27
与大小为2 GB的csv一起工作良好。
–纪律
16年11月8日,3:10
对于尺寸大约为11k x 5k的矩阵文件,我发现transpose.c的速度比ghostdog74的第一个awk解决方案快约7倍,内存效率高约5倍。另外,我发现ghostdog74的“几乎不使用内存” awk代码无法正常工作。另外,请注意transpose.c程序中的--limit标志,默认情况下会将输出限制为1k x 1k。
–ncemami
16-11-28在6:40
#5 楼
看看可以像datamash transpose
一样使用的GNU datamash。未来的版本还将支持交叉列表(数据透视表)
#6 楼
纯BASH,无需其他过程。一个不错的练习:declare -a array=( ) # we build a 1-D-array
read -a line < "" # read the headline
COLS=${#line[@]} # save number of columns
index=0
while read -a line ; do
for (( COUNTER=0; COUNTER<${#line[@]}; COUNTER++ )); do
array[$index]=${line[$COUNTER]}
((index++))
done
done < ""
for (( ROW = 0; ROW < COLS; ROW++ )); do
for (( COUNTER = ROW; COUNTER < ${#array[@]}; COUNTER += COLS )); do
printf "%s\t" ${array[$COUNTER]}
done
printf "\n"
done
评论
这对我的文件有用,尽管有趣的是它为表的第一行打印了一个目录列表。我不知道足够的BASH找出原因。
–面包
2013年6月19日19:50
@bugloaf,您的桌子的角落有一个*。
– Hello71
2014年8月27日23:26
@bugloaf:正确地引用变量应该可以防止这种情况:printf“%s \ t”“ $ {array [$ COUNTER]}”
–丹尼斯·威廉姆森
2014年11月26日在16:12
#7 楼
这是完成此工作的中等可靠的Perl脚本。 @ ghostdog74的awk
解决方案有许多结构类比。 )。对于较大的数据集(100x100矩阵,每个条目6-8个字符),perl的效果稍差于awk-0.026s和0.042s。两者都可能不会造成问题。 MacOS X 10.5.8上的32位),文件包含10,000行,每行5列: ,但仍比perl慢。显然,您的里程会有所不同。评论
在我的系统上,gawk优于perl。您可以在编辑后的帖子中看到我的结果
–ghostdog74
09年11月16日在9:34
得出的结论是:不同的平台,不同的软件版本,不同的结果。
–ghostdog74
09年11月16日在16:11
#8 楼
GNU datamash仅需一行代码并可能具有任意大文件大小,因此非常适合此问题!#9 楼
为此有一个专门构建的实用程序,GNU datamash实用程序
apt install datamash
datamash transpose < yourfile
来自此站点https://www.gnu。 org / software / datamash /和http://www.thelinuxrain.com/articles/transposed-rows-and-columns-3-methods
#10 楼
如果安装了sc
,则可以执行以下操作:psc -r < inputfile | sc -W% - > outputfile
评论
请注意,这支持有限的行数,因为sc将其列命名为一个字符或两个字符的组合。限制为26 + 26 ^ 2 = 702。
–雷神
2012年11月8日上午10:38
#11 楼
假设您所有的行都具有相同数量的字段,那么这个awk程序可以解决以下问题:一个':'分隔的字符串
f
,其中包含该字段的元素。完成所有行之后,将这些字符串中的每个字符串打印在单独的一行中。然后,通过将输出通过col[f]
传递给管道,可以用':'代替所需的分隔符(例如,空格)。示例:
#12 楼
骇人的perl解决方案可以是这样的。很好,因为它不会加载内存中的所有文件,不会打印中间的临时文件,然后使用所有精彩的粘贴信息。评论
使用粘贴和临时文件只是多余的操作。您可以在内存本身内部进行操作,例如数组/哈希
–ghostdog74
09年11月13日在17:11
是的,但这不是意味着将所有内容都保留在内存中吗?我正在处理的文件大小约为2-20GB。
– Federico Giorgi
09年11月16日在11:49
#13 楼
对于您自己的示例,我可以看到的唯一改进是使用awk,它将减少运行的进程数以及在它们之间传递的数据量:#14 楼
我通常使用这个小的awk
代码段来满足此要求:转置给定的输入。 这需要跟踪初始文件的最大列数,以便将其用作要打印回的行数。
#15 楼
一些* nix标准的util一线式,不需要临时文件。注意:OP需要一个有效的解决方案(即更快),并且最常见的答案通常比该答案更快。这些单行代码是出于各种原因而喜欢* nix软件工具的用户的。在极少数情况下(例如,IO和内存不足),这些摘要实际上可能比某些顶级答案要快。将输入文件命名为foo。 >
如果我们知道foo有四列:
for f in 1 2 3 4 ; do cut -d ' ' -f $f foo | xargs echo ; done
如果我们不知道foo有多少列:
n=$(head -n 1 foo | wc -w)
for f in $(seq 1 $n) ; do cut -d ' ' -f $f foo | xargs echo ; done
xargs
有大小限制,因此使用长文件将无法完成工作。大小限制取决于系统,例如:{ timeout '.01' xargs --show-limits ; } 2>&1 | grep Max
我们可以实际使用的最大命令长度:2088944
tr
和echo
:for f in 1 2 3 4; do cut -d ' ' -f $f foo | tr '\n\ ' ' ; echo; done
...或者列数未知:
n=$(head -n 1 foo | wc -w)
for f in $(seq 1 $n); do
cut -d ' ' -f $f foo | tr '\n' ' ' ; echo
done
使用
set
(与xargs
类似)具有类似的基于命令行大小的限制:评论
所有这些都将比awk或perl解决方案慢几个数量级并且脆弱。阅读unix.stackexchange.com/questions/169716/…。
–埃德·莫顿(Ed Morton)
16-4-10的15:25
@EdMorton,谢谢,成功回答了我对您的速度问题的回答。关于“脆弱”:不是3),并且当程序员知道数据对于给定技术是安全的时,也不是其他。难道POSIX兼容的shell代码不是比perl更稳定的标准吗?
–agc
16年4月10日在18:17
抱歉,我对perl非常了解。在这种情况下,使用的工具将是awk。 cut,head,echo等与awk脚本相比,与POSIX兼容的外壳代码没有更多,它们都是UNIX安装中的标准配置。完全没有理由使用一组工具,这些工具组合在一起时,您只需要使用awk时,就需要小心输入文件的内容和执行脚本的目录,并且最终结果更快且更可靠。
–埃德·莫顿(Ed Morton)
16-4-10在19:12
拜托,我不是抗辩,但条件各不相同。原因1:对于在切割头xargs seq awk中的f;做wc -c $(哪个$ f);完成当存储速度太慢或IO太低时,更大的解释器会使情况变得更糟,无论在更理想的情况下它们的性能如何。原因2:awk(或大多数语言)也比设计成一件事的小型utils承受着更陡峭的学习曲线。如果运行时间比编码器工时便宜,那么使用“软件工具”进行简单编码就可以节省成本。
–agc
16-4-10在20:30
#16 楼
我使用了fgm的解决方案(感谢fgm!),但是需要消除每行末尾的制表符,因此对脚本进行了如下修改:#17 楼
我只是在寻找类似的bash转置,但支持填充。这是我根据fgm的解决方案编写的脚本,看起来很有效。如果有帮助...#!/bin/bash
declare -a array=( ) # we build a 1-D-array
declare -a ncols=( ) # we build a 1-D-array containing number of elements of each row
SEPARATOR="\t";
PADDING="";
MAXROWS=0;
index=0
indexCol=0
while read -a line; do
ncols[$indexCol]=${#line[@]};
((indexCol++))
if [ ${#line[@]} -gt ${MAXROWS} ]
then
MAXROWS=${#line[@]}
fi
for (( COUNTER=0; COUNTER<${#line[@]}; COUNTER++ )); do
array[$index]=${line[$COUNTER]}
((index++))
done
done < ""
for (( ROW = 0; ROW < MAXROWS; ROW++ )); do
COUNTER=$ROW;
for (( indexCol=0; indexCol < ${#ncols[@]}; indexCol++ )); do
if [ $ROW -ge ${ncols[indexCol]} ]
then
printf $PADDING
else
printf "%s" ${array[$COUNTER]}
fi
if [ $((indexCol+1)) -lt ${#ncols[@]} ]
then
printf $SEPARATOR
fi
COUNTER=$(( COUNTER + ncols[indexCol] ))
done
printf "\n"
done
#18 楼
我一直在寻找一种解决方案,可以将任何类型的矩阵(nxn或mxn)与任何类型的数据(数字或数据)进行转置,并得到以下解决方案:Row2Trans=number1
Col2Trans=number2
for ((i=1; $i <= Line2Trans; i++));do
for ((j=1; $j <=Col2Trans ; j++));do
awk -v var1="$i" -v var2="$j" 'BEGIN { FS = "," } ; NR==var1 {print $((var2)) }' $ARCHIVO >> Column_$i
done
done
paste -d',' `ls -mv Column_* | sed 's/,//g'` >> $ARCHIVO
#19 楼
如果您只想从文件中抓取一个单行(以逗号分隔)$ N并将其变成一列:#20 楼
不是很优雅,但是这个“单行”命令可以快速解决问题:#21 楼
另一个awk
解决方案和有限的输入(具有您的内存大小)。第一列中的行,第二列中的第二行,等等。输出:
awk '{ for (i=1; i<=NF; i++) RtoC[i]= (RtoC[i]? RtoC[i] FS $i: $i) }
END{ for (i in RtoC) print RtoC[i] }' infile
#22 楼
#!/bin/bash
aline="$(head -n 1 file.txt)"
set -- $aline
colNum=$#
#set -x
while read line; do
set -- $line
for i in $(seq $colNum); do
eval col$i="\"$col$i $$i\""
done
done < file.txt
for i in $(seq $colNum); do
eval echo ${col$i}
done
带有
set
的另一个版本eval
评论
阅读unix.stackexchange.com/questions/169716/…,以了解该解决方案的部分但不是全部问题。
–埃德·莫顿(Ed Morton)
16年4月10日在15:43
#23 楼
另一个bash变体$ cat file
XXXX col1 col2 col3
row1 0 1 2
row2 3 4 5
row3 6 7 8
row4 9 10 11
脚本
#!/bin/bash
I=0
while read line; do
i=0
for item in $line; { printf -v A$I[$i] $item; ((i++)); }
((I++))
done < file
indexes=$(seq 0 $i)
for i in $indexes; {
J=0
while ((J<I)); do
arr="A$J[$i]"
printf "${!arr}\t"
((J++))
done
echo
}
输出
$ ./test
XXXX row1 row2 row3 row4
col1 0 3 6 9
col2 1 4 7 10
col3 2 5 8 11
#24 楼
这是Haskell解决方案。用-O2编译时,对于重复的“ Hello world”输入行,它的运行速度比ghostdog的awk略快,并且比Stephan的薄包装c python略慢。不幸的是,据我所知,GHC不支持传递命令行代码,因此您必须自己将其写入文件。它将截断行到最短行的长度。transpose :: [[a]] -> [[a]]
transpose = foldr (zipWith (:)) (repeat [])
main :: IO ()
main = interact $ unlines . map unwords . transpose . map words . lines
#25 楼
一种将整个数组存储在内存中的awk解决方案 awk '#!/bin/bash
maxf="$(awk '{if (mf<NF); mf=NF}; END{print mf}' infile)"
rowcount=maxf
for (( i=1; i<=rowcount; i++ )); do
awk -v i="$i" -F " " '{printf("%s\t ", $i)}' infile
echo
done
!~/^$/{ i++;
split(q4312078q,arr,FS);
for (j in arr) {
out[i,j]=arr[j];
if (maxr<j){ maxr=j} # max number of output rows.
}
}
END {
maxc=i # max number of output columns.
for (j=1; j<=maxr; j++) {
for (i=1; i<=maxc; i++) {
printf( "%s:", out[i,j])
}
printf( "%s\n","" )
}
}' infile
但是我们可以根据需要的输出行“遍历”文件多次: /> q4312078q
(对于较少的输出行,它比以前的代码要快)。
#26 楼
这是一个Bash单行代码,其基础是将每行简单地转换为一列,然后对其进行paste
-inging:echo '' > tmp1; \
cat m.txt | while read l ; \
do paste tmp1 <(echo $l | tr -s ' ' \n) > tmp2; \
cp tmp2 tmp1; \
done; \
cat tmp1
m.txt:
0 1 2
4 5 6
7 8 9
10 11 12
创建
tmp1
文件,因此它不为空。读取每一行,并使用
tr
将其转换为列。 tmp1
文件将结果复制回
tmp1
中。PS:我真的很想使用io描述符,但无法使它们工作。
评论
如果要在大文件上执行闹钟,请确保设置闹钟。阅读unix.stackexchange.com/questions/169716/…以了解该方法的部分但不是全部问题。
–埃德·莫顿(Ed Morton)
16年4月10日在15:46
#27 楼
使用R ...的内衬...#28 楼
我以前在下面两个脚本中使用过类似的操作。第一个在awk中,比第二个在“纯” bash中快得多。您可能可以使其适应您自己的应用程序。awk '
{
for (i = 1; i <= NF; i++) {
s[i] = s[i]?s[i] FS $i:$i
}
}
END {
for (i in s) {
print s[i]
}
}' file.txt
declare -a arr
while IFS= read -r line
do
i=0
for word in $line
do
[[ ${arr[$i]} ]] && arr[$i]="${arr[$i]} $word" || arr[$i]=$word
((i++))
done
done < file.txt
for ((i=0; i < ${#arr[@]}; i++))
do
echo ${arr[i]}
done
#29 楼
简单的4行答案,保持可读性。col="$(head -1 file.txt | wc -w)"
for i in $(seq 1 $col); do
awk '{ print $'$i' }' file.txt | paste -s -d "\t"
done
评论
是什么让您认为存在一个bash脚本要比Perl脚本快?这正是Perl解决的问题。@mark,如果纯粹是bash,它可能比将所有cut / sed等工具链接在一起的速度更快。但是话又说回来,如果您在组合工具中定义“ bash”,那么仅编写awk脚本就可以与Perl wrt文本处理媲美。
添加另一个原因是不了解Perl在这里的运行速度。编写代码慢吗?执行慢吗?我确实不喜欢perl,但是在这种任务上确实很出色。
如果您的列/字段具有固定的大小/宽度,则可以使用Python文件搜寻来避免将文件读入内存。您有固定的列/字段大小/宽度吗?
任何认为shell脚本比awk或perl都要快的人都需要阅读unix.stackexchange.com/questions/169716/…,这样他们才能理解为什么不是这种情况。