Supprimer les valeurs numériques dans certaines colonnes tout en conservant les signes moins?


9

J'ai le bloc de données suivant qui continue indéfiniment horizontalement et verticalement avec des nombres négatifs uniquement dans les colonnes impaires:

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

Et je veux les 2ème, 4ème et 6ème colonnes complètes (ou chaque colonne paire) et les signes moins uniquement à partir des 1ère, 3ème et 5ème (ou toutes les colonnes impaires), donc j'obtiens ceci:

- 2   4 - 9
  3 - 5 - 11

Et finalement finir avec ceci:

-2  4 -9
 3 -5 -11

J'ai donc besoin que les valeurs des colonnes paires soient inchangées et des colonnes impaires, s'il y a une valeur négative, conservez le - uniquement et s'il y a une valeur positive, jetez-la.

Existe-t-il un moyen de le faire avec awk / sed?

C'est à peu près autant que je reçois:

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

Lorsque vous dites que votre trame de données continue indéfiniment, voulez-vous dire horizontalement ou verticalement? Combien de colonnes avez-vous réellement?
terdon

Tous les deux. Mes données de test sont de 3 lignes par 3 colonnes mais les données réelles ont des nombres variables, je dirais jusqu'à 40 lignes et 40 colonnes.
Trouvé le

Réponses:


2

Voici une façon:

$ 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

Le awkscript parcourt toutes les colonnes impaires et définit leur valeur -si elles sont négatives et vides sinon. Ensuite, le sedsupprime tous les espaces suivant un -, puis remplace plusieurs espaces consécutifs par un seul. Notez que cela signifie que l'alignement sera rompu car certains champs auront deux caractères ou plus et d'autres en auront un. Ce ne sera pas un problème si vous travaillez avec des champs, ils ne sont tout simplement pas jolis.


4

Le sedchemin:

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

Production:

-2  4 -9
 3 -5 -11

La première expression tue la colonne de fin s'il y a un nombre impair de colonnes. Il le fait en recherchant 0 ou plusieurs paires <number> <number>, où le premier nombre peut être négatif.

Edit: Une sedsolution plus courte , inspirée de @mikeserv:

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

La même chose avec perl:

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

Une autre façon perl(probablement la plus propre):

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

Cela fonctionne très bien sur mes données réelles tant que j'ajoute les points décimaux dans le script. Merci!
Trouvé le

@Asfound Ok, j'ai modifié ma réponse pour prendre également en charge les points décimaux.
lcd047

Attendez, cela échouera s'il y a une valeur négative comme dernier champ (impair).
terdon

@terdon Il échoue s'il y a un nombre impair de colonnes, oui. Mais il y a soit exactement 6 colonnes, soit "infiniment nombreuses", et "infiniment plusieurs" n'est pas un nombre impair. :)
lcd047

L'OP a déclaré qu'il peut y avoir "jusqu'à 40 colonnes" :(
terdon

3

Un perl:

$ perl -anle 'BEGIN{$,=" "}
  print map{$_=$F[$_]=~/^-/?"-$F[$_+1]":" $F[$_+1]"}grep{!($_%2)}0..$#F' file
-2  4 -9
 3 -5 -11
  • -andiviser l'entrée en @Ftableau
  • BEGIN{$,=" "} définir un séparateur de champ de sortie sur un espace
  • grep{!($_%2)}0..$#Fobtenir tous les index pairs dans un @Ftableau, qui sont des index d'éléments impairs
  • map{$_=$F[$_]=~/^-/?"-$F[$_+1]":" $F[$_+1]"}vérifier si l'élément impair commence par -, puis ajouter -à l'élément pair suivant, sinon ajouter un espace

3

Comme la réponse de @ terdon mais sans le sed:

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

3

Une pythonsolution

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

Une awksolution simple basée sur les mathématiques :

$ 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
  • Boucle du deuxième ( i=2) au dernier champ ( i<=NF).
  • Multipliez le champ précédent ( $(i-1)) par -1 ou 1.
  • Formatez bien la sortie ( printf "%4s") et imprimez une nouvelle ligne de fin ( print "").

La seule mise en garde est que si vous avez un nombre impair de colonnes, le dernier champ n'affichera rien du tout. J'espère que c'est ce que vous attendez. Apparemment, c'est ce que vous attendez. :)

(modifié pour fonctionner avec des valeurs décimales et pour rendre les conditions de boucle plus alignées avec la question tout en enregistrant 2 caractères.)


1

Vous devez oublier complètement le négatif - laissez-le de côté. Vous souhaitez consolider deux champs - de gauche à droite. C'est très simple.

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

Remarquez comment j'évite toute référence au signe - lorsque l'entrée est traitée, l'automate n'accepte que des espaces ou des chiffres car il ne comprend rien d'autre - tout le reste est complètement ignoré et reste en place.

Lorsque vous spécifiez un \{intervalle de répétition numérique \}pour une \(sous - expression \), seule la dernière occurrence de cette expression est \1référencée en retour. Vous pouvez donc simplement presser - ou tronquer - un intervalle de répétition aussi facilement. Et parce que nous pressons la répétition derrière le signe - s'il y en a un - la deuxième occurrence de ce motif suivra tout signe qui précédait le premier.

Le comportement décrit ci-dessus est spécifié par POSIX pour toutes les applications compatibles BRE, mais très peu sedle font correctement. GNU le sedfait.

Enfin, les espaces sont juste pour rendre l'occurrence du motif régulière .

Bien sûr, cela ne fonctionnera jamais pour vous. Ou, probablement plus correctement, cela fonctionnera toujours pour vous, mais ne retournera aucun résultat. Comment pourrait-il si le motif est indéfini ?


Cela ne fonctionnera que s'il existe un nombre pair de champs.
terdon

@terdon - non - ça marche pour n'importe quoi.
mikeserv

Non, essayez-le avec un nombre impair de champs. Le dernier est imprimé et ne devrait pas l'être.
terdon

@terdon - pourquoi ne devrait-il pas en être ainsi? Il n'y a pas de champ suivant pour l'annuler? Le demandeur déclare qu'il souhaite supprimer les colonnes impaires suivies d'une colonne paire. La dernière colonne n'est pas suivie d'une colonne paire - elle fait exactement ce qu'elle devrait et supprime le moins possible. À mon avis, supposer que certaines données devraient disparaître est une mauvaise pratique.
mikeserv

Non, ils ne le font pas: "J'ai donc besoin que les valeurs des colonnes paires soient inchangées et des colonnes impaires, s'il y a une valeur négative, conservez le - uniquement et s'il y a une valeur positive, jetez-la." Les champs impairs ne doivent jamais être imprimés, la seule information qu'ils doivent communiquer est s'ils sont négatifs. Le vôtre imprime des champs impairs positifs.
terdon
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.