Menghapus nilai numerik di kolom tertentu sambil menyimpan tanda minus?

9

Saya memiliki kerangka data berikut yang berlanjut secara tak terbatas secara horizontal dan vertikal dengan angka negatif hanya di kolom ganjil:

-1  2  3  4 -5  9
 2  3 -4  5 -6  11

Dan saya ingin 2, 4 dan 6 kolom lengkap (atau setiap kolom genap) dan tanda minus hanya dari 1, 3, dan 5 (atau setiap kolom aneh), jadi saya mendapatkan ini:

- 2   4 - 9
  3 - 5 - 11

Dan akhirnya berakhir dengan ini:

-2  4 -9
 3 -5 -11

Jadi saya perlu nilai dari kolom genap tidak berubah dan kolom aneh, jika ada nilai negatif, pertahankan - saja dan jika ada nilai positif, buanglah.

Apakah ada cara untuk melakukan ini dengan awk / sed?

Ini tentang sejauh yang saya dapatkan:

awk '{ for (i=2;i<=NF;i+=2) $i="" }1' FILE.txt | sed 's/[0-9,.]*//g' 
Asfound
sumber
Ketika Anda mengatakan kerangka data Anda berlanjut tanpa batas, apakah maksud Anda secara horizontal atau vertikal? Berapa banyak kolom yang Anda miliki?
terdon
Kedua. Data pengujian saya adalah 3 baris dengan 3 kolom tetapi data aktual memiliki angka yang bervariasi, saya akan mengatakan hingga 40 baris dan 40 kolom.
Asfound

Jawaban:

2

Ini salah satu caranya:

$ awk '{for(i=1;i<=NF;i+=2){if($i<0){$i="-"}else{$i="";} }};1' file |
     sed 's/- */-/g; s/  */ /g'
-2 4 -9
 3 -5 -11

The awkScript berjalan di atas semua kolom aneh dan menetapkan nilai mereka untuk -jika mereka negatif dan mengosongkan jika tidak. Kemudian, sedmenghapus ruang apa pun setelah a -dan kemudian menggantikan beberapa ruang berturut-turut dengan satu. Perhatikan bahwa ini berarti pelurusan akan rusak karena beberapa bidang akan memiliki dua karakter atau lebih dan yang lain akan memiliki satu. Itu tidak akan menjadi masalah jika Anda bekerja dengan bidang, mereka hanya tidak terlihat cantik.

terdon
sumber
4

The sedcara:

sed -E '
    s/^(([ \t]*-?[ \t]*[0-9.]+[ \t]+[0-9.]+)*)[ \t]+-?[ \t]*[0-9.]+$/\1/;
    s/[0-9.]+[ \t]+([0-9.]+)/\1/g'

Keluaran:

-2  4 -9
 3 -5 -11

Ekspresi pertama membunuh kolom tambahan jika ada jumlah kolom ganjil. Itu melakukannya dengan mencari 0 atau lebih pasangan <number> <number>, di mana angka pertama bisa negatif.

Sunting:sed Solusi yang lebih pendek , terinspirasi oleh @mikeserv:

sed -E '
    s/[0-9.]+[ \t]*([0-9.]*)/\1/g;
    s/[- \t]*$//'

Hal yang sama dengan perl:

perl -lpe 's/^((\s*-?\s*[\d.]+\s*[\d.]+)*)\s+-?\s*[\d.]+$/$1/o; s/[\d.]+\s+([\d.]+)/$1/g'

Cara lain dengan perl(mungkin yang terbersih):

perl -lpe '$a = 1; s/([\d.]+\s*)/$a++ % 2 ? "" : $1/eg; s/[-\s]*$//o'
lcd047
sumber
Ini berfungsi baik pada data aktual saya selama saya menambahkan titik desimal ke dalam skrip. Terima kasih!
Asfound
@ Asfound Ok, saya mengedit jawaban saya untuk juga mendukung poin desimal.
lcd047
Tunggu dulu, ini akan gagal jika ada nilai negatif sebagai bidang (ganjil) terakhir.
terdon
@terdon Gagal jika ada jumlah kolom ganjil, ya. Tetapi ada tepat 6 kolom, atau "banyak sekali", dan "banyak sekali" bukan angka ganjil. :)
lcd047
OP mengatakan bahwa mungkin ada "hingga 40 kolom" :(
terdon
3

Satu perl:

$ perl -anle 'BEGIN{$,=" "}
  print map{$_=$F[$_]=~/^-/?"-$F[$_+1]":" $F[$_+1]"}grep{!($_%2)}0..$#F' file
-2  4 -9
 3 -5 -11
  • -anpisahkan input ke @Farray
  • BEGIN{$,=" "} mengatur pemisah bidang keluaran ke spasi
  • grep{!($_%2)}0..$#Fdapatkan semua indeks genap dalam @Farray, yang merupakan indeks elemen aneh
  • map{$_=$F[$_]=~/^-/?"-$F[$_+1]":" $F[$_+1]"}periksa apakah elemen aneh dimulai dengan -, lalu tambahkan -ke elemen genap berikutnya, atau tambahkan spasi
cuonglm
sumber
3

Sebagai jawaban @ terdon tetapi tanpa sed:

awk '{ for(i=1;i<=NF;i+=2){
         if ($i<0) $(i+1)*=-1;
         $i = "";
       }
       print
     }'
meuh
sumber
3

Sebuah pythonsolusi

python -c 'from __future__ import print_function; 
import sys, math;
for line in sys.stdin:
  x = [int(y) for y in line.split()]
  print(*[int(math.copysign(b, a)) for a, b in zip(x[::2], x[1::2])], sep=" ")
' <file
iruvar
sumber
2

Solusi sederhana berbasis matematika awk:

$ cat <<M | awk '{for(i=2;i<=NF;i+=2){printf "%4s",($(i-1)<0?-1:1)*$i}print ""}'
-1  2  3  4 -5  9
2  3.2 -4  5 -6
M

  -2   4  -9
 3.2  -5
  • Loop dari bidang kedua ( i=2) ke bidang terakhir ( i<=NF).
  • Lipat gandakan bidang sebelumnya ( $(i-1)) dengan -1 atau 1.
  • Format output dengan baik ( printf "%4s"), dan cetak baris tambahan ( print "").

Satu-satunya peringatan untuk ini adalah bahwa jika Anda memiliki jumlah kolom ganjil, bidang terakhir tidak akan menampilkan apa pun. Saya harap ini yang Anda harapkan. Ternyata inilah yang Anda harapkan. :)

(diedit untuk bekerja dengan nilai desimal, dan untuk membuat kondisi loop lebih selaras dengan pertanyaan sambil menyimpan 2 karakter.)

hjk
sumber
1

Anda harus melupakan yang negatif sepenuhnya - tinggalkan saja. Anda ingin menggabungkan dua bidang - dari kiri ke kanan. Itu sangat mudah.

sed '   s/ *\(.*\)/\1 /
        s/\([0-9]*  *\)\{2\}/\1/g
        s/[ -]*$//
' <<\IN
-1  2  3  4 -5  9
 2  3 -4  5 -6  11
IN
-2  4 -9
3 -5 -11

Perhatikan bagaimana saya menghindari referensi ke tanda sama sekali - ketika input diproses automaton hanya akan menerima spasi atau angka karena tidak mengerti apa-apa lagi - semuanya diabaikan sepenuhnya dan akan tetap berada di tempatnya.

Saat Anda menentukan \{interval pengulangan numerik \}untuk \(subekspresi \), hanya kemunculan terakhir dari ekspresi yang \1direferensikan kembali. Jadi Anda bisa menekan - atau memotong - interval ulang yang mudah. Dan karena kita menekan pengulangan di belakang tanda - jika ada - kemunculan kedua dari pola itu akan mengikuti tanda apa pun yang digunakan untuk mendahului yang pertama.

Perilaku yang dijelaskan di atas ditentukan oleh POSIX untuk semua aplikasi yang sesuai dengan BRE, tetapi sangat sedikit yang sedmelakukannya dengan benar. GNU sedmelakukannya.

Terakhir, spasi hanya untuk membuat pola terjadi secara teratur .

Tentu saja, ini tidak akan pernah berhasil untuk Anda. Atau, mungkin lebih tepat, itu akan selalu berhasil untuk Anda, tetapi tidak pernah memberikan hasil apa pun. Bagaimana mungkin jika polanya tidak pasti ?

mikeserv
sumber
Ini hanya akan berfungsi jika ada sejumlah bidang.
terdon
@terdon - tidak - itu berfungsi untuk apa pun.
mikeserv
Tidak, coba dengan jumlah bidang ganjil. Yang terakhir dicetak dan seharusnya tidak.
terdon
@terdon - mengapa tidak? Tidak ada bidang berikut untuk membatalkannya? Penanya menyatakan mereka ingin menghapus kolom ganjil diikuti oleh kolom genap. Kolom terakhir tidak diikuti oleh kolom genap - ia melakukan apa yang seharusnya, dan menghapus sesedikit mungkin. Dengan asumsi beberapa data harus pergi adalah praktik buruk menurut saya.
mikeserv
Tidak, mereka tidak: "Jadi saya perlu nilai dari kolom genap tidak berubah dan dari kolom aneh, jika ada nilai negatif, pertahankan - saja dan jika ada nilai positif, buanglah." Bidang ganjil tidak boleh dicetak, satu-satunya informasi yang harus mereka berikan adalah apakah negatif. Anda mencetak bidang aneh positif.
terdon