Saya memiliki file besar yang dipisahkan tab dengan format seperti ini
X column1 column2 column3
row1 0 1 2
row2 3 4 5
row3 6 7 8
row4 9 10 11
Saya ingin mengubah urutannya dengan cara yang efisien hanya dengan menggunakan perintah bash (saya dapat menulis sepuluh atau lebih baris skrip Perl untuk melakukannya, tetapi harus lebih lambat untuk dieksekusi daripada fungsi bash asli). Jadi hasilnya akan terlihat seperti
X row1 row2 row3 row4
column1 0 3 6 9
column2 1 4 7 10
column3 2 5 8 11
Saya memikirkan solusi seperti ini
cols=`head -n 1 input | wc -w`
for (( i=1; i <= $cols; i++))
do cut -f $i input | tr $'\n' $'\t' | sed -e "s/\t$/\n/g" >> output
done
Tapi itu lambat dan sepertinya bukan solusi yang paling efisien. Saya telah melihat solusi untuk vi di posting ini , tetapi masih terlalu lambat. Ada pemikiran / saran / ide brilian? :-)
Jawaban:
keluaran
Kinerja terhadap solusi Perl oleh Jonathan pada file 10.000 baris
EDIT oleh Ed Morton (@ ghostdog74 jangan ragu untuk menghapusnya jika Anda tidak setuju).
Mungkin versi ini dengan beberapa nama variabel yang lebih eksplisit akan membantu menjawab beberapa pertanyaan di bawah ini dan secara umum menjelaskan apa yang dilakukan skrip. Ini juga menggunakan tab sebagai pemisah yang awalnya diminta OP sehingga akan menangani bidang kosong dan secara kebetulan meningkatkan sedikit keluaran untuk kasus khusus ini.
Solusi di atas akan bekerja di sembarang awk (kecuali awk lama, rusak tentu saja - ada YMMV).
Solusi di atas memang membaca seluruh file ke dalam memori - jika file input terlalu besar untuk itu maka Anda dapat melakukan ini:
yang menggunakan hampir tidak ada memori tetapi membaca file input sekali per jumlah kolom dalam satu baris sehingga akan jauh lebih lambat daripada versi yang membaca seluruh file ke dalam memori. Ini juga mengasumsikan jumlah bidang sama pada setiap baris dan menggunakan GNU awk for
ENDFILE
danARGIND
tetapi awk apa pun dapat melakukan hal yang sama dengan pengujian padaFNR==1
danEND
.sumber
Opsi lainnya adalah menggunakan
rs
:-c
mengubah pemisah kolom masukan,-C
mengubah pemisah kolom keluaran, dan-T
mengubah urutan baris dan kolom. Jangan gunakan-t
alih-alih-T
, karena menggunakan jumlah baris dan kolom yang dihitung secara otomatis yang biasanya tidak benar.rs
, yang dinamai fungsi pembentukan ulang di APL, hadir dengan BSD dan OS X, tetapi seharusnya tersedia dari manajer paket di platform lain.Opsi kedua adalah menggunakan Ruby:
Opsi ketiga adalah menggunakan
jq
:jq -R .
mencetak setiap baris masukan sebagai literal string JSON,-s
(--slurp
) membuat larik untuk baris masukan setelah mengurai setiap baris sebagai JSON, dan-r
(--raw-output
) mengeluarkan konten string alih-alih literal string JSON. The/
operator kelebihan beban untuk string split.sumber
rs
- terima kasih untuk penunjuknya! ( Tautannya ke Debian; bagian hulu tampaknya mirbsd.org/MirOS/dist/mir/rs )rs
yang datang dengan OS X,-c
sendiri set pemisah kolom input ke tab.$'\t'
TTC TTA TTC TTC TTT
, menjalankanrs -c' ' -C' ' -T < rows.seq > cols.seq
memberirs: no memory: Cannot allocate memory
. Ini adalah sistem yang menjalankan FreeBSD 11.0-RELEASE dengan 32 GB ram. Jadi, tebakan saya adalahrs
menempatkan semuanya dalam RAM, yang bagus untuk kecepatan, tetapi tidak untuk data yang besar.Solusi Python:
Di atas didasarkan pada yang berikut:
Kode ini mengasumsikan bahwa setiap baris memiliki jumlah kolom yang sama (tidak ada padding yang dilakukan).
sumber
l.split()
denganl.strip().split()
(Python 2.7), jika tidak, baris terakhir dari output akan lumpuh. Berfungsi untuk pemisah kolom arbitrer, gunakanl.strip().split(sep)
dansep.join(c)
jika pemisah Anda disimpan dalam variabelsep
.yang transpose proyek sourceforge adalah seperti coreutil C program yang tepat.
sumber
-b
dan-f
.BASH murni, tanpa proses tambahan. Latihan yang bagus:
sumber
printf "%s\t" "${array[$COUNTER]}"
Lihat GNU datamash yang bisa digunakan seperti
datamash transpose
. Versi mendatang juga akan mendukung tabulasi silang (tabel pivot)sumber
Berikut ini skrip Perl yang cukup solid untuk melakukan pekerjaan itu. Ada banyak analogi struktural dengan
awk
solusi @ ghostdog74 .Dengan ukuran data sampel, perbedaan kinerja antara perl dan awk dapat diabaikan (1 milidetik dari total 7). Dengan kumpulan data yang lebih besar (matriks 100x100, masing-masing entri 6-8 karakter), perl sedikit mengungguli awk - 0,026s vs 0,042s. Tidak ada yang mungkin menjadi masalah.
Pengaturan waktu representatif untuk Perl 5.10.1 (32-bit) vs awk (versi 20040207 ketika diberi '-V') vs gawk 3.1.7 (32-bit) di MacOS X 10.5.8 pada file yang berisi 10.000 baris dengan 5 kolom per baris:
Perhatikan bahwa melongo jauh lebih cepat dari awk di mesin ini, tapi masih lebih lambat dari perl. Jelas, jarak tempuh Anda akan bervariasi.
sumber
Jika Anda telah
sc
menginstal, Anda dapat melakukan:sumber
sc
menamai kolomnya sebagai satu atau kombinasi dari dua karakter. Batasannya adalah26 + 26^2 = 702
.Ada utilitas yang dibangun khusus untuk ini,
Utilitas GNU datamash
Diambil dari situs ini, https://www.gnu.org/software/datamash/ dan http://www.thelinuxrain.com/articles/transposing-rows-and-columns-3-methods
sumber
Dengan asumsi semua baris Anda memiliki jumlah bidang yang sama, program awk ini menyelesaikan masalah:
Dengan kata lain, saat Anda mengulang baris, untuk setiap bidang, buat
f
':' - string terpisah yangcol[f]
berisi elemen bidang itu. Setelah Anda selesai dengan semua baris, cetak setiap string tersebut di baris terpisah. Anda kemudian dapat mengganti ':' untuk pemisah yang Anda inginkan (katakanlah, spasi) dengan menyalurkan keluarantr ':' ' '
.Contoh:
sumber
GNU datamash sangat cocok untuk masalah ini dengan hanya satu baris kode dan kemungkinan besarnya ukuran file yang sewenang-wenang!
sumber
Solusi perl hackish bisa seperti ini. Itu bagus karena tidak memuat semua file di memori, mencetak file temp menengah, dan kemudian menggunakan pasta yang sangat bagus
sumber
Satu-satunya peningkatan yang dapat saya lihat pada contoh Anda sendiri adalah menggunakan awk yang akan mengurangi jumlah proses yang dijalankan dan jumlah data yang disalurkan di antara mereka:
sumber
Saya biasanya menggunakan
awk
potongan kecil ini untuk persyaratan ini:Ini hanya memuat semua data ke dalam array dua dimensi
a[line,column]
dan kemudian mencetaknya kembali sebagaia[column,line]
, sehingga mentransposisi input yang diberikan.Ini perlu melacak
max
jumlah kolom yang dimiliki file awal, sehingga digunakan sebagai jumlah baris untuk dicetak kembali.sumber
Saya menggunakan solusi fgm (terima kasih fgm!), Tetapi perlu menghilangkan karakter tab di akhir setiap baris, jadi ubah skripnya menjadi:
sumber
Saya hanya mencari pesta yang serupa tetapi dengan dukungan untuk bantalan. Berikut adalah skrip yang saya tulis berdasarkan solusi fgm, yang tampaknya berfungsi. Jika itu bisa membantu ...
sumber
Saya sedang mencari solusi untuk mengubah semua jenis matriks (nxn atau mxn) dengan semua jenis data (angka atau data) dan mendapatkan solusi berikut:
sumber
Jika Anda hanya ingin mengambil satu baris (dipisahkan koma) $ N dari file dan mengubahnya menjadi kolom:
sumber
Tidak terlalu elegan, tetapi perintah "satu baris" ini menyelesaikan masalah dengan cepat:
Di sini kolom adalah jumlah kolom, di mana Anda dapat mengganti 4 dengan
head -n 1 input | wc -w
.sumber
awk
Solusi lain dan input terbatas dengan ukuran memori yang Anda miliki.Ini menggabungkan setiap posisi nomor yang sama menjadi bersama-sama dan
END
mencetak hasil yang akan menjadi baris pertama di kolom pertama, baris kedua di kolom kedua, dll. Akan menampilkan:sumber
Beberapa standar * nix menggunakan satu baris, tidak perlu file temporer. NB: OP menginginkan perbaikan yang efisien , (yaitu lebih cepat), dan jawaban teratas biasanya lebih cepat dari jawaban ini. Ini satu-liners adalah untuk mereka yang suka * nix perangkat lunak , untuk alasan apapun. Dalam kasus yang jarang terjadi, ( mis. IO & memori yang langka), cuplikan ini sebenarnya bisa lebih cepat daripada beberapa jawaban teratas.
Panggil file input foo .
Jika kita tahu foo memiliki empat kolom:
Jika kita tidak tahu berapa banyak kolom yang dimiliki foo :
xargs
memiliki batas ukuran dan karena itu akan membuat pekerjaan tidak lengkap dengan file yang panjang. Berapa batas ukuran yang bergantung pada sistem, misalnya:tr
&echo
:... atau jika # kolom tidak diketahui:
Menggunakan
set
, yang sepertixargs
, memiliki batasan berbasis ukuran baris perintah yang serupa:sumber
awk
.cut
,head
,echo
, Dll tidak lebih POSIX kompatibel kode shell dari sebuahawk
script - mereka semua adalah standar pada setiap instalasi UNIX. Tidak ada alasan untuk menggunakan seperangkat alat yang dalam kombinasi tersebut mengharuskan Anda untuk berhati-hati tentang konten file input Anda dan direktori tempat Anda menjalankan skrip ketika Anda dapat menggunakan awk dan hasil akhirnya lebih cepat serta lebih kuat .for f in cut head xargs seq awk ; do wc -c $(which $f) ; done
Ketika penyimpanan terlalu lambat atau IO terlalu rendah, penerjemah yang lebih besar memperburuk keadaan tidak peduli seberapa baik mereka dalam keadaan yang lebih ideal. Alasan # 2: awk , (atau sebagian besar bahasa lainnya), juga mengalami kurva belajar yang lebih curam daripada util kecil yang dirancang untuk melakukan satu hal dengan baik. Ketika waktu proses lebih murah daripada jam kerja pembuat kode, pengkodean mudah dengan "perangkat lunak" dapat menghemat uang.versi lain dengan
set
eval
sumber
Varian bash lainnya
Naskah
Keluaran
sumber
Inilah solusi Haskell. Ketika dikompilasi dengan -O2, ini berjalan sedikit lebih cepat dari awk ghostdog dan sedikit lebih lambat dari python
c yang dibungkus tipisStephan pada mesin saya untuk jalur input "Halo dunia" yang berulang. Sayangnya dukungan GHC untuk meneruskan kode baris perintah sejauh yang saya tahu tidak ada, jadi Anda harus menuliskannya ke file sendiri. Ini akan memotong baris menjadi panjang baris terpendek.sumber
Solusi awk yang menyimpan seluruh array dalam memori
Tetapi kita dapat "menjalankan" file tersebut sebanyak baris keluaran yang dibutuhkan:
Yang (untuk jumlah baris keluaran yang rendah lebih cepat dari kode sebelumnya).
sumber
Berikut ini Bash one-liner yang didasarkan pada konversi setiap baris ke kolom dan
paste
menggabungkannya:m.txt:
membuat
tmp1
file jadi tidak kosong.membaca setiap baris dan mengubahnya menjadi kolom menggunakan
tr
menempelkan kolom baru ke
tmp1
filesalinan hasil kembali menjadi
tmp1
.PS: Saya benar-benar ingin menggunakan io-descriptors tetapi tidak bisa membuatnya berfungsi.
sumber
Seorang oneliner menggunakan R ...
sumber
Saya telah menggunakan dua skrip di bawah ini untuk melakukan operasi serupa sebelumnya. Yang pertama di awk yang jauh lebih cepat daripada yang kedua di bash "murni". Anda mungkin dapat menyesuaikannya dengan aplikasi Anda sendiri.
sumber