J'essaie d'utiliser unix pour transformer un fichier délimité par des tabulations d'un format court / large au format long, de la même manière que la fonction de remodelage de R. J'espère créer trois lignes pour chaque ligne du fichier de départ. La colonne 4 contient actuellement 3 valeurs séparées par des virgules. J'espère garder les colonnes 1, 2 et 3 identiques pour chaque ligne de départ, mais que la colonne 4 soit l'une des valeurs de la colonne initiale 4. Cet exemple le rend probablement plus clair que je ne peux le décrire verbalement:

current file:  
A1  A2  A3  A4,A5,A6  
B1  B2  B3  B4,B5,B6  
C1  C2  C3  C4,C5,C6  

goal:  
A1  A2  A3  A4  
A1  A2  A3  A5  
A1  A2  A3  A6  
B1  B2  B3  B4  
B1  B2  B3  B5  
B1  B2  B3  B6  
C1  C2  C3  C4  
C1  C2  C3  C5  
C1  C2  C3  C6  

En tant que personne se familiarisant avec cette langue, ma première pensée était d'utiliser sed pour trouver les virgules remplacer par un retour dur

sed 's/,/&\n/' data.frame

Je ne sais vraiment pas comment inclure les valeurs des colonnes 1 à 3. J'avais peu d'espoir que cela fonctionne, mais la seule chose à laquelle je pouvais penser était d'essayer d'insérer les valeurs de colonne avec {print $ 1, $ 2, $ 3}.

sed 's/,/&\n{print $1, $2, $3}/' data.frame

Pas à ma grande surprise, la sortie ressemblait à ceci:

A1  A2  A3  A4  
{print $1, $2, $3}  A5  
{print $1, $2, $3}  A6  
B1  B2  B3  B4  
{print $1, $2, $3}  B5  
{print $1, $2, $3}  B6  
C1  C2  C3  C4  
{print $1, $2, $3}  C5  
{print $1, $2, $3}  C6  

Il semble qu'une approche pourrait consister à stocker les valeurs des colonnes 1 à 3, puis à les insérer. Je ne sais pas vraiment comment stocker les valeurs, je pense que cela peut impliquer l'utilisation d'une adaptation du script suivant, mais j'ai du mal à comprendre tous les composants.

NR==FNR{a[$1, $2, $3]=1}

Merci d'avance pour vos réflexions à ce sujet.

0
user4670961 25 janv. 2017 à 05:12

4 réponses

Meilleure réponse

Vous pouvez écrire une simple boucle read pour cela et utiliser l'expansion d'accolades pour analyser le champ délimité par des virgules:

#!/bin/bash

while read -r f1 f2 f3 c1; do
  # split the comma delimited field 'c1' into its constituents
  for c in ${c1//,/ }; do
     printf "$f1 $f2 $f3 $c\n"
  done
done < input.txt

Production:

A1 A2 A3 A4
A1 A2 A3 A5
A1 A2 A3 A6
B1 B2 B3 B4
B1 B2 B3 B5
B1 B2 B3 B6
C1 C2 C3 C4
C1 C2 C3 C5
C1 C2 C3 C6
2
codeforester 25 janv. 2017 à 05:50

Dans le grand Miller, il y a le verbe nest pour le faire

Avec

mlr --nidx --ifs "\t" nest --explode --values  --across-records -f 4 --nested-fs ","  input.tsv

Tu vas avoir

A1 A2 A3 A4
A1 A2 A3 A5
A1 A2 A3 A6
B1 B2 B3 B4
B1 B2 B3 B5
B1 B2 B3 B6
C1 C2 C3 C4
C1 C2 C3 C5
C1 C2 C3 C6
0
aborruso 10 mai 2019 à 08:49

Si vous n'avez pas besoin que la sortie soit dans un ordre particulier dans un groupe de la quatrième colonne, le awk one-liner suivant peut faire l'affaire:

awk '{split($4,a,","); for(i in a) print $1,$2,$3,a[i]}' input.txt

Cela fonctionne en divisant votre 4ème colonne en un tableau, puis pour chaque élément du tableau, en imprimant les «nouvelles» quatre colonnes.

Si l'ordre est important - c'est-à-dire que A4 doit venir avant A5, etc., vous pouvez utiliser une boucle for classique:

awk '{split($4,a,","); for(i=1;i<=length(a);i++) print $1,$2,$3,a[i]}' input.txt

Mais c'est bizarre. Et vous parlez de bash.

Les éléments suivants pourraient fonctionner:

#!/usr/bin/env bash

mapfile -t arr < input.txt

for s in "${arr[@]}"; do
  t=($s)
  mapfile -t -d, u <<<"${t[3]}"
  for v in "${u[@]}"; do
    printf '%s %s %s %s\n' "${t[@]:0:3}" "${v%$'\n'}"
  done
done

Cela copie votre fichier d'entrée entier dans les éléments d'un tableau, puis parcourt ce tableau, mappant chaque 4ème colonne dans un deuxième tableau. Il parcourt ensuite ce deuxième tableau, imprimant les trois premières colonnes du premier tableau, ainsi que le champ actuel du deuxième tableau.

Sa structure est évidemment similaire à celle de l'alternative awk, mais beaucoup plus lourde à lire et à coder.

Notez le ${v%$'\n'} sur la ligne printf. Cela supprime la nouvelle ligne de fin du dernier champ, qui n'est pas supprimée par mapfile car nous utilisons un autre délimiteur.

Notez également qu'il n'y a aucune raison pour laquelle vous avez de copier toutes vos entrées dans un tableau, je l'ai juste fait de cette façon pour démontrer un peu plus de mapfile. Vous pouvez bien sûr utiliser l'ancienne norme,

while read s; do
   ...
done < input.txt

Si tu préfères.

0
ghoti 25 janv. 2017 à 06:15

Comme solution sans appeler un programme externe:

#!/bin/bash

data_file="d"

while IFS=" " read -r f1 f2 f3 r
do
  IFS="," read f4 f5 f6 <<<"$r"
  printf "$f1 $f2 $f3 $f4\n$f1 $f2 $f3 $f5\n$f1 $f2 $f3 $f6\n"
done <"$data_file"
1
Fred 25 janv. 2017 à 03:07