Question

J'ai un fichier texte layed comme ceci:

1   a, b, c
2   c, b, c
2.5 a, c

Je voudrais inverser les touches (le nombre) et les valeurs (CSV) (ils sont séparés par un caractère de tabulation) pour produire ceci:

a   1, 2.5
b   1, 2
c   1, 2, 2.5

(Remarquez comment 2 n'est pas dupliqué pour c.)

Je ne ai pas besoin cette sortie exacte. Les chiffres de l'entrée sont commandés, alors que les valeurs ne sont pas. Les clés de la sortie doivent être commandés, ainsi que les valeurs.

Comment puis-je faire cela? J'ai accès aux services de shell standard (awk, sed, grep ...) et du CCG. Je peux probablement saisir un compilateur / interpréteur pour d'autres langues si nécessaire.

Était-ce utile?

La solution

Si vous avez python (si vous êtes sur Linux, vous avez probablement déjà) j'utiliser un script python court pour le faire. Notez que nous utilisons des ensembles pour filtrer les éléments « double ».

Sous la direction d'être plus proche des exigences de la demande:

import csv
from decimal import * 
getcontext().prec = 7

csv_reader = csv.reader(open('test.csv'), delimiter='\t')

maindict = {}
for row in csv_reader:
    value = row[0]
    for key in row[1:]:
        try:
            maindict[key].add(Decimal(value))
        except KeyError:
            maindict[key] = set()
        maindict[key].add(Decimal(value))

csv_writer = csv.writer(open('out.csv', 'w'), delimiter='\t')

sorted_keys = [x[1] for x in sorted([(x.lower(), x) for x in maindict.keys()])]
for key in sorted_keys:
    csv_writer.writerow([key] + sorted(maindict[key]))

Autres conseils

Je voudrais essayer perl si c'est à votre disposition. Boucle à travers l'entrée d'une ligne à la fois. Diviser la ligne sur l'onglet puis la partie de la main droite sur les virgules. Shove les valeurs dans un tableau associatif avec les lettres que les clés et la valeur étant un tableau associatif. Le second tableau associatif va jouer le rôle d'un ensemble de manière à éliminer les doublons.

Une fois que vous avez lu le fichier d'entrée, tri en fonction des clés du tableau associatif, boucle à travers et cracher sur les résultats.

Voici un petit utilitaire en php:

// load and parse the input file
$data = file("path/to/file/");
foreach ($data as $line) {
    list($num, $values) = explode("\t", $line);
    $newData["$num"] = explode(", ", trim($values));
}
unset($data);

// reverse the index/value association
foreach ($newData as $index => $values) {
    asort($values);
    foreach($values as $value) {
        if (!isset($data[$value]))
            $data[$value] = array();
        if (!in_array($index, $data[$value]))
            array_push($data[$value], $index);
    }
}

// printout the result
foreach ($data as $index => $values) {
    echo "$index\t" . implode(", ", $values) . "\n";
}   

pas vraiment optimisé ou beau, mais il fonctionne ...

# use Modern::Perl;
use strict;
use warnings;
use feature qw'say';


our %data;

while(<>){
  chomp;
  my($number,$csv) = split /\t/;
  my @csv = split m"\s*,\s*", $csv;
  push @{$data{$_}}, $number for @csv;
}

for my $number (sort keys %data){
  my @unique = sort keys %{{ map { ($_,undef) } @{$data{$number}} }};
  say $number, "\t", join ', ', @unique;
}

Voici un exemple en utilisant le module Text :: CSV de CPAN plutôt que l'analyse manuelle des champs CSV:

use strict;
use warnings;
use Text::CSV;

my %hash;
my $csv = Text::CSV->new({ allow_whitespace => 1 });

open my $file, "<", "file/to/read.txt";

while(<$file>) {
  my ($first, $rest) = split /\t/, $_, 2;
  my @values;

  if($csv->parse($rest)) {
    @values = $csv->fields()
  } else {
    warn "Error: invalid CSV: $rest";
    next;
  }

  foreach(@values) {
    push @{ $hash{$_} }, $first;
  }
}

# this can be shortened, but I don't remember whether sort()
# defaults to <=> or cmp, so I was explicit
foreach(sort { $a cmp $b } keys %hash) {
  print "$_\t", join(",", sort { $a <=> $b } @{ $hash{$_} }), "\n";
}

Notez qu'il imprime sur la sortie standard. Je recommande simplement rediriger la sortie standard, et si vous développez tout ce programme, assurez-vous d'utiliser pour imprimer les warn() erreurs, plutôt que les ing print(). En outre, il ne vérifie pas les entrées en double, mais je ne veux pas faire mon regard de code comme Brad Gilbert, qui ressemble un peu wack même à un Perlite.

Voici un awk (1) et le tri (1) réponse:

Vos données sont essentiellement un grand nombre à plusieurs ensemble de données de sorte que la première étape consiste à normaliser les données avec une clé et la valeur par ligne. Nous allons également échanger les clés et valeurs pour indiquer le nouveau champ primaire, mais ce n'est pas strictement nécessaire que les parties plus bas ne dépendent pas de l'ordre. Nous utilisons un onglet ou [espaces], [espaces] comme séparateur de champ pour que nous divisons sur l'onglet entre la clé et les valeurs, et entre les valeurs. Cela laissera des espaces intégrés dans les valeurs, mais les couper avant et après:

awk -F '\t| *, *' '{ for (i=2; i<=NF; ++i) { print $i"\t"$1 } }'

Alors nous voulons appliquer votre ordre de tri et d'éliminer les doublons. Nous utilisons une fonction bash pour spécifier un onglet char comme séparateur (-t $ \ « t »). Si vous utilisez Bourne shell / POSIX, vous devez utiliser '[tab]', où [tab] est un onglet littéral:

sort -t $'\t' -u -k 1f,1 -k 2n

Ensuite, remettre sous la forme que vous voulez:

awk -F '\t' '{ 
    if (key != $1) { 
        if (key) printf "\n";
        key=$1;
        printf "%s\t%s", $1, $2
    } else {
        printf ", %s", $2
    }
  }
  END {printf "\n"}'

Tuyauterie eux tout à fait et vous devriez obtenir votre sortie désirée. Je l'ai testé avec les outils GNU.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top