bagaimana cara mengurutkan secara alfanumerik di Unix dengan sortir? Lebih rumit dari yang terlihat

Saya mencoba mengurutkan serangkaian huruf dan angka secara alfanumerik dengan cara "intuitif"/alami menggunakan perintah unix sort, tetapi tidak dapat mengurutkannya dengan benar. Saya punya file ini:

$ cat ~/headers 
@42EBKAAXX090828:6:100:1699:328/2
@42EBKAAXX090828:6:10:1077:1883/2
@42EBKAAXX090828:6:102:785:808/2

Saya ingin mengurutkannya secara alfanumerik, yang secara intuitif @42EBKAAXX090828:6:10:... adalah yang pertama (karena 10 lebih kecil dari 100 dan 102), yang kedua adalah @42EBKAAXX090828:6:100... dan yang ketiga adalah @42EBKAAXX090828:6:102:204:1871/2.

Saya tahu bahwa menyarankan pengurutan pada posisi tertentu dalam garis, tetapi posisi : di sini dapat bervariasi sehingga ini bukan solusi umum dan bisa diterapkan di sini.

Saya mencoba:

sort --stable -k1,1 ~/headers > foo

dengan berbagai kombinasi parameter -n dan -u tetapi tidak memberikan urutan yang benar.

Bagaimana ini bisa dilakukan secara efisien, baik dari bash menggunakan sort atau dari Python? Saya ingin menerapkan ini pada file yang berukuran sekitar 4-5 GB, sehingga berisi jutaan baris.

Terima kasih!


person Community    schedule 06.12.2011    source sumber
comment
FYI, ini biasa disebut Sortasi Alami.   -  person yak    schedule 06.12.2011
comment
Tidak yakin dengan performanya, tapi inilah implementasi pengurutan alami dengan python: stackoverflow.com/q/4836710/331473   -  person Adam Wagner    schedule 06.12.2011
comment
bagaimana Anda menangani @42EBKAAXX09082*7*:6:100:1699:328/2 dan @42EBKAAXX09082*8*:6:100:1699:328/2 (*s untuk penekanan)? apakah mereka diurutkan sama? (yaitu hanya bidang ke-3 yang relevan) maka jawaban @JonathanM adalah yang terbaik. Kalau tidak, lihat milikku   -  person tobyodavies    schedule 06.12.2011


Jawaban (3)


opsi -V tampaknya melakukan apa yang Anda inginkan - penyortiran alami. Rupanya ditujukan untuk nomor versi (karenanya huruf yang dipilih)

sort -V ~/headers

keluaran

@42EBKAAXX090828:6:10:1077:1883/2
@42EBKAAXX090828:6:100:1699:328/2
@42EBKAAXX090828:6:102:785:808/2
person tobyodavies    schedule 06.12.2011
comment
yang mana yang TIDAK TERDOKUMENTASI ~~~~~ - person user528025; 07.06.2013
comment
@ user528025 tidak, ini didokumentasikan, saya menemukan opsi ini dengan mencari halaman manual untuk pengurutan alami. - person tobyodavies; 11.06.2013

Ini adalah mengurutkannya berdasarkan abjad seperti pada contoh Anda. Alasan 10: muncul setelah 100 dan 102 adalah karena 10: ada setelahnya, karena titik dua : berada setelah karakter 9 di Bagan ASCII.

Jika Anda ingin mengurutkan pada kolom ketiga yang dibatasi oleh titik dua, coba ini:

sort -t':' -k3 ~/headers > foo
person Jonathan M    schedule 06.12.2011
comment
Jawaban bagus jika OP hanya ingin mengurutkan bidang itu - person tobyodavies; 06.12.2011
comment
Mungkin lebih baik menggunakan -k3n atau -k3,4n sehingga 9 mengurutkan sebelum 10. Ada ruang untuk berpikir bahwa OP mungkin ingin '@43ZQRY101112:6:19:221:134/3' mengurutkan berdasarkan baris yang ditampilkan daripada di tempat kedua, jadi pengurutan mungkin perlu dilakukan pada lebih banyak kunci daripada hanya pada kunci ketiga. Menarik untuk mengetahui apakah data '@6NBGD010101:9:99:999:111/3' atau '@213QED081231:16:91:23:2/0' dapat muncul, dan di mana data tersebut akan muncul relatif terhadap baris mulai '@42E'. Permasalahannya masih belum terinci karena kita tidak memiliki gambaran lengkap tentang variabilitas data yang masuk. - person Jonathan Leffler; 06.12.2011
comment
@JonathanLeffler, sangat mungkin. Komentar yang bagus. Terima kasih. - person Jonathan M; 06.12.2011
comment
@JonathanLeffler: terima kasih telah menunjukkan hal ini. Untuk lebih jelasnya, urutannya adalah memperlakukan ':' seolah-olah itu adalah urutan besaran, dari tinggi ke rendah, dalam angka desimal. Jadi 6:500 mendahului 7:10 dan 2:200 mendahului 6:500. Inilah sebabnya menurut saya solusi pengurutan berdasarkan nomor kolom titik dua tertentu tidak akan berhasil. Apakah ada alternatif lain selain itu? terima kasih. - person ; 06.12.2011
comment
jawaban yang paling benar untuk ini adalah saya ingin identik dengan cara kerja program samtools dengan samtools sort -n option sort, tapi sayangnya saya tidak dapat menemukan deskripsi yang tepat tentang prosedur penyortiran apa yang digunakannya di mana pun... apakah ada yang tahu? ? - person ; 06.12.2011
comment
@ user248237: Apakah Anda peduli dengan urutan relatif bidang sebelum titik dua pertama? Apakah bidang tersebut merupakan bidang pengurutan yang paling penting atau paling tidak penting? Dan apakah 'urutan kumpulan kode lurus' OK untuk bidang pertama? Jika Anda hanya memerlukan 4 bidang setelah titik dua pertama hingga garis miring yang diurutkan dalam urutan numerik, maka sort -t: -k2,3n -k3,4n -k4,5n -k5,6n sudah cukup. Jika Anda perlu memperlakukan bidang pertama secara khusus, maka bidang tersebut akan menjadi lebih kompleks. - person Jonathan Leffler; 06.12.2011
comment
Ada makalah indah 'Teori dan Praktek dalam Konstruksi Rutinitas Pengurutan Kerja' oleh J P Linderman. Pada akhirnya, ini menunjukkan bahwa cara terbaik (atau, setidaknya, cara yang baik) untuk meningkatkan kinerja pengurutan pada kunci yang kompleks adalah dengan menulis kode transformasi yang menempatkan kunci di depan setiap baris dalam format yang mudah diurutkan, lalu mengumpankan data ke pengurutan utama, lalu menghapus kunci pengurutan dari jalur keluaran. Hal ini mengurangi biaya tambahan dalam menafsirkan setiap baris, sehingga membuat penyortiran menjadi lebih cepat. Sayangnya, makalah ini sulit ditemukan di internet; Google menemukan referensi tetapi tidak menemukan makalahnya. - person Jonathan Leffler; 06.12.2011
comment
@ user248237 jika Anda ingin : bertindak sebagai pemisah urutan besarnya, lihat jawaban saya. - person tobyodavies; 06.12.2011

Ini biasanya disebut Penyortiran Alami. Inilah salah satu cara yang berhasil untuk contoh kumpulan data Anda.

import re

def natural_sorted(iterable, reverse=False):
    """Return a list sorted the way that humans expect."""
    def convert(text):
        return int(text) if text.isdigit() else text
    def natural(item):
        return map(convert, re.split('([0-9]+)', item))
    return sorted(iterable, key=natural, reverse=reverse)

Saya menemukan ini di sini dan sedikit membaik.

person yak    schedule 06.12.2011
comment
akankah ini ditingkatkan dibandingkan dengan jenis Unix? Bisakah ini bekerja pada file sepanjang jutaan baris? - person ; 06.12.2011