Entfernen numerischer Werte in bestimmten Spalten unter Beibehaltung von Minuszeichen?


9

Ich habe den folgenden Datenrahmen, der horizontal und vertikal unbegrenzt mit negativen Zahlen nur in den ungeraden Spalten fortgesetzt wird:

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

Und ich möchte die 2., 4. und 6. vollständige Spalte (oder jede gerade Spalte) und die Minuszeichen nur von der 1., 3. und 5. (oder jeder ungeraden Spalte), also bekomme ich Folgendes:

- 2   4 - 9
  3 - 5 - 11

Und am Ende damit:

-2  4 -9
 3 -5 -11

Ich brauche also die Werte aus den geraden Spalten unverändert und aus den ungeraden Spalten. Wenn es einen negativen Wert gibt, behalte den - only und wenn es einen positiven Wert gibt, verwerfe ihn.

Gibt es eine Möglichkeit, dies mit awk / sed zu tun?

Dies ist ungefähr so ​​weit wie ich komme:

awk '{ for (i=2;i<=NF;i+=2) $i="" }1' FILE.txt | sed 's/[0-9,.]*//g' 

Wenn Sie sagen, dass Ihr Datenrahmen auf unbestimmte Zeit fortgesetzt wird, meinen Sie damit horizontal oder vertikal? Wie viele Spalten haben Sie tatsächlich?
Terdon

Beide. Meine Testdaten sind 3 Zeilen mal 3 Spalten, aber die tatsächlichen Daten haben unterschiedliche Zahlen, ich würde sagen, 40 Zeilen und 40 Spalten.
Am

Antworten:


2

Hier ist eine Möglichkeit:

$ 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

Das awkSkript geht alle ungeraden Spalten durch und setzt ihren Wert auf, -wenn sie negativ und leer sind, wenn nicht. Anschließend werden sedalle Leerzeichen nach a entfernt -und mehrere aufeinanderfolgende Leerzeichen durch ein einzelnes ersetzt. Beachten Sie, dass dies bedeutet, dass die Ausrichtung unterbrochen wird, da einige Felder zwei oder mehr Zeichen und andere eines haben. Das ist kein Problem, wenn Sie mit Feldern arbeiten, sie sehen einfach nicht hübsch aus.


4

Der sedWeg:

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

Ausgabe:

-2  4 -9
 3 -5 -11

Der erste Ausdruck beendet die nachfolgende Spalte, wenn eine ungerade Anzahl von Spalten vorhanden ist. Dazu werden 0 oder mehr Paare gesucht <number> <number>, wobei die erste Zahl negativ sein kann.

Bearbeiten: Eine kürzere sedLösung, inspiriert von @mikeserv:

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

Das gleiche mit perl:

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

Ein anderer Weg mit perl(wahrscheinlich dem saubersten):

perl -lpe '$a = 1; s/([\d.]+\s*)/$a++ % 2 ? "" : $1/eg; s/[-\s]*$//o'

Dies funktioniert gut mit meinen tatsächlichen Daten, solange ich die Dezimalstellen in das Skript einfüge. Vielen Dank!
Am

@Asfound Ok, ich habe meine Antwort bearbeitet, um auch Dezimalstellen zu unterstützen.
lcd047

Warte, dies schlägt fehl, wenn als letztes (ungerades) Feld ein negativer Wert vorliegt.
Terdon

@terdon Es schlägt fehl, wenn es eine ungerade Anzahl von Spalten gibt, ja. Aber es gibt entweder genau 6 Spalten oder "unendlich viele", und "unendlich viele" ist keine ungerade Zahl. :)
lcd047

Das OP sagte, dass es "bis zu 40 Spalten" geben kann :(
terdon

3

Eine perlEins:

$ perl -anle 'BEGIN{$,=" "}
  print map{$_=$F[$_]=~/^-/?"-$F[$_+1]":" $F[$_+1]"}grep{!($_%2)}0..$#F' file
-2  4 -9
 3 -5 -11
  • -anEingabe in @FArray aufteilen
  • BEGIN{$,=" "} Setzen Sie das Ausgabefeldtrennzeichen auf ein Leerzeichen
  • grep{!($_%2)}0..$#FHolen Sie sich alle geraden Indizes im @FArray, die Indizes von ungeraden Elementen sind
  • map{$_=$F[$_]=~/^-/?"-$F[$_+1]":" $F[$_+1]"}Überprüfen Sie, ob ein ungerades Element mit beginnt -, und hängen Sie es -an das nächste gerade Element an. Andernfalls fügen Sie ein Leerzeichen hinzu

3

Als Antwort von @ terdon, aber ohne sed:

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

3

Eine pythonLösung

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

2

Eine einfache mathematikbasierte awkLösung:

$ 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
  • Schleife vom zweiten ( i=2) zum letzten Feld ( i<=NF).
  • Multiplizieren Sie das vorherige Feld ( $(i-1)) mit -1 oder 1.
  • Formatieren Sie die Ausgabe schön ( printf "%4s") und drucken Sie eine nachfolgende Newline ( print "").

Die einzige Einschränkung besteht darin, dass im letzten Feld bei einer ungeraden Anzahl von Spalten überhaupt nichts angezeigt wird. Ich hoffe das ist was du erwartest. Anscheinend ist es das, was Sie erwarten. :) :)

(bearbeitet, um mit Dezimalwerten zu arbeiten und die Schleifenbedingungen besser an die Frage anzupassen, während 2 Zeichen gespeichert werden.)


1

Sie müssen das Negative ganz vergessen - lassen Sie es weg. Sie möchten zwei Felder konsolidieren - von links nach rechts. Das ist sehr einfach.

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

Beachten Sie, wie ich jegliche Bezugnahme auf das Zeichen überhaupt vermeide - wenn die Eingabe verarbeitet wird, akzeptiert der Automat nur Leerzeichen oder Zahlen, weil er nichts anderes versteht - alles andere wird vollständig ignoriert und bleibt an Ort und Stelle.

Wenn Sie ein \{numerisches Wiederholungsintervall \}für einen \(Unterausdruck angeben \), wird nur auf das letzte Vorkommen dieses Ausdrucks \1verwiesen. So können Sie ein Wiederholungsintervall so einfach drücken oder abschneiden. Und weil wir die Wiederholung hinter dem Zeichen drücken - falls es eines gibt - folgt das zweite Auftreten dieses Musters jedem Zeichen, das vor dem ersten vorangestellt war.

Das oben beschriebene Verhalten wird von POSIX für alle BRE-kompatiblen Anwendungen angegeben, aber nur sehr wenige sedmachen es richtig. GNU sedtut es.

Zuletzt dienen die Leerzeichen nur dazu, das Auftreten des Musters regelmäßig zu machen .

Natürlich wird dies bei Ihnen niemals funktionieren. Oder, wahrscheinlich richtiger, es wird immer für Sie funktionieren, aber niemals Ergebnisse zurückgeben. Wie könnte es sein, wenn das Muster unbestimmt ist ?


Dies funktioniert nur, wenn eine gerade Anzahl von Feldern vorhanden ist.
Terdon

@terdon - nein - es funktioniert für was auch immer.
Mikesserv

Nein, versuchen Sie es mit einer ungeraden Anzahl von Feldern. Der letzte ist gedruckt und sollte es nicht sein.
Terdon

@terdon - warum sollte es nicht sein? Es gibt kein folgendes Feld, um es aufzuheben? Der Fragesteller gibt an, dass ungerade Spalten gefolgt von einer geraden Spalte entfernt werden sollen. Auf die letzte Spalte folgt keine gerade Spalte - sie macht genau das, was sie sollte, und entfernt so wenig wie möglich. Die Annahme, dass einige Daten gelöscht werden sollten, ist meiner Meinung nach eine schlechte Praxis.
Mikeserv

Nein, das tun sie nicht: "Also brauche ich die Werte aus den geraden Spalten unverändert und aus den ungeraden Spalten. Wenn es einen negativen Wert gibt, behalte den - nur und wenn es einen positiven Wert gibt, verwerfe ihn." Ungerade Felder sollten niemals gedruckt werden. Die einzige Information, die sie vermitteln sollten, ist, ob sie negativ waren. Ihre druckt positive ungerade Felder.
Terdon
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.