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' 
Wie gefunden
quelle
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.

terdon
quelle
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'
lcd047
quelle
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
cuonglm
quelle
3

Als Antwort von @ terdon, aber ohne sed:

awk '{ for(i=1;i<=NF;i+=2){
         if ($i<0) $(i+1)*=-1;
         $i = "";
       }
       print
     }'
meuh
quelle
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
iruvar
quelle
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.)

hjk
quelle
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 ?

mikeserv
quelle
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