Werte addieren über Dateigrenzen hinweg

E

elyps

Hallo,

ich hoffe, dass der Titel das Problem bereits deutlich macht.

Ich habe mehrere ASCII-Dateien, die jeweils 21x21 Zahlenwerte beinhalten. Ich möchte nun beliebige Dateien "addieren", also diese Matrizen addieren.

Ich habe leider nichts zu diesem spezifischen Fall gefunden, sondern immer nur das (für mich) recht umständliche Addieren mittels awk:
Code:
#!/bin/awk -f
BEGIN{OFS="\t";}
{column1[FNR]+=$1;column2[FNR]+=$2;column3[FNR]+=$3;}
END {
for (i in column1) {print i, column1[i], column2[i], column3[i]}
}
Bei dieser Methode muss ich aber alle columns manuell angeben. Das erscheint mir nicht gerade effektiv.

Weiß jemand von euch Hilfe? Danke.
 
Zuletzt bearbeitet von einem Moderator:
Keine Ahnung, ob das für dich brauchbar ist. Geht sicher viel schöner. Habe es jetzt nur von etwas bestehendem abgeleitet und hingefrickelt.

Code:
#!/usr/bin/ruby

# Usage: add_matrix.rb eins.txt zwei.txt [drei.txt ...]

add_matrix = []
width_matrix = 21

if __FILE__== $0
  # Read input
  if ARGV.length > 1
      ARGV.each do |file|
      if FileTest.readable?(file)
        pos = 0
        File.read(file).scan(/[\d]{1,4}/).each do |element|
          if add_matrix[pos]
            add_matrix[pos] += element.to_i
          else
            add_matrix[pos] = element.to_i
          end
          pos += 1
        end
      end
    end
  end

  # Form and print output
  pos = 0
  add_matrix.each do |element|
    pos += 1
    print element
    if pos == width_matrix
      print "\n"
      pos = 0
    else
      print "\t"
    end
  end

end

cu
 
Zuletzt bearbeitet:
Schnellschuss, aber probier doch mal so was:
Code:
#!/bin/bash

exec 3<$1
exec 4<$2

while read L1 <&3; read L2 <&4; do
        set -- $L1
        for N in $L2; do
                echo -n "$(($1 + $N)) "
                shift
        done
        echo
done

Gruss, A.
 
So, jetzt komme ich endlich mal zum Ausprobieren...

Schnellschuss, aber probier doch mal so was:
...

Ein Aufruf dieses Skripts mit den beiden Matrizen als Argumente führt zu folgender Fehlermeldung:
Code:
./matrix_addition.sh: Zeile 9: 8.382520E-09 + 3.914659E-09:
Syntaxfehler: Ungültiger arithmetischer Operator.
(error token is ".382520E-09 + 3.914659E-09")
hmm, kapiere ich nicht so ganz... wieso wird der erste Wert nicht vollständig wiedergegeben?
 
War halt ein Schnellschuss ;-)

Problem ist, dass die Shell intern nur Arithmetik mit Ganzzahlen beherrscht. Daher zeigt er dir den Teil der ersten Zahl ab dem Dezimaltrenner als Fehler an.

Wenn du also in o.a. Skript die Zeile
Code:
echo -n "$(($N + $1)) "
durch
Code:
echo $N $1 | awk '{ printf "%e ", $1 + $2; }'
ersetzt, sollte das schon mal ein bisschen besser laufen. Das Problem ist dann allerdings, dass du bei den 21x21-Matrizen mehr als 400-mal den awk bemühst ...

Innerhalb eines awk-Skripts sollte es wohl möglich sein, mit "getline" jeweils eine Zeile aus der ersten und zweiten Eingabedatei zu lesen.

Ein anderer Versuch wäre evtl, zunächst mit "paste" die beiden Matrizen zeilenweise zusammenzufassen, und dann mit "awk" zu verarbeiten:
Code:
#!/bin/sh

paste $1 $2 |
awk '{
        for (i = 1; i <= NF/2; i++) {
                j = i + NF/2;
                printf "%e%s", $i + $j, i < NF/2 ? "\t" : "\n";
        }
}'

Gruss
 
Hallo floyd62.

Vielen Dank! Das ist echt super und noch besser... es funktioniert! :-)

Hast Du noch ne Idee, wie ich dem Skript sagen kann, dass es immer mehrere Dateien, die den gleichen Namen haben, addieren soll? Konkret habe ich vier Ordner und in jedem dieser Ordner liegen dutzende Dateien, die jeweils gleich benannt sind... nun will ich dem Skript sagen, dass es sich jeweils die gleich-benannten Dateien aus diesen Ordnern holt, diese dann addiert (nach obigem Modell) und das Ergebnis unter einem neuen Namen in einen neuen (fünften) Ordner speichert.

Ich weiß... eine sehr spezielle Frage, sorry.

Gruss.
 
Zuletzt bearbeitet von einem Moderator:
... einfach ' ne kleine Schleife um den awk von oben:
Code:
IN1=...
IN2=...
OUT=...

for F1 in IN1/*; do
        B=$(basename "$F1")
        F2=IN2/$B
        [ -f "$F2" ] || continue
        paste "$F1" "$F2" |
        awk '
                for (i = 1; i <= NF/2; i++) {
                        j = i + NF/2;
                        printf "%e%s", $i + $j, i < NF/2 ? "\t" : "\n";
                }
        ' > $OUT/$B
done
Wie immer: ungetestet ...

Gruss
 
Hi floyd62,

ich weiß nicht woran es liegt, weil es keine Fehlermeldung gibt, aber es passiert anscheinend nix bei dem obigen Skript.

Viele Grüße.
 
Bei der Verwendung der IN1 und IN2-Variablen oben fehlt jeweils der $, sollte also z.B. so aussehen
Code:
for F in $IN1/*; do
...
        F2=$IN2/$B
um die Dateien in den Verzeichnissen $IN1 und $IN2 zu addieren und das Ergebnis nach $OUT zu schieben. Die übrigen Bastelarbeiten (temporäre Dateien definieren, wenn du die Matrizen aus 4 Verzeichnissen addieren willst etc.) überlasse ich dir dann aber selbst ...

Grüsse
 
Hi,

vielen Dank, das hatte ich übersehen mit dem $. Ich danke Dir sehr für Deine Hilfe und würde es ja auch gerne erweitern, aber ich seh den Wald vor lauter Bäumen nicht, wie mir scheint. Eigentlich ist es ja ganz einfach... das Pasten mittels
Code:
paste $1 $2 |
awk '{
        for (i = 1; i <= NF/2; i++) {
                j = i + NF/2;
                printf "%e%s", $i + $j, i < NF/2 ? "\t" : "\n";
        }
}'
klappt wunderbar, aber für die Schleife kommt ein Synthaxfehler:
Code:
awk: line 2: syntax error at or near for
für jede Datei, die aus der Addition entsteht. Die Dateien werden zwar in dem neuen Ordner angelegt, sind aber alle leer :-(

Viele Grüße.
 
Zeig doch einfach mal das ganze Skript, das du bisher hast. Der Teil, den du in deiner letzten Nachricht zitierst, läuft bei mir hier einwandfrei .... aber der verarbeitet ja weder Dateien aus irgendwelchen Ordnern, noch legt er Ausgabedateien in einem anderen Verzeichnis ab. Ich vermute also doch, dass der eigentliche Fehler ganz woanders liegt.

Gruss
 
Ja, der letzte Teil läuft bei mir auch, wenn ich im als Argument zwei Dateien zum Addieren gebe. Das gesamte Skript allerdings
Code:
#!/bin/bash

IN1=45_auswertung	
IN2=135_auswertung
OUT=test

for F1 in $IN1/*; do
        B=$(basename "$F1")
        F2=$IN2/$B
        [ -f "$F2" ] || continue
        paste "$F1" "$F2" |
        awk '
                for (i = 1; i <= NF/2; i++) {
                        j = i + NF/2;
                        printf "%e%s", $i + $j, i < NF/2 ? "\t" : "\n";
                }
        ' > $OUT/$B
done
spuckt den besagten Fehler aus. Zumindest legt er schon einmal leere Dateien an, prinzipiell läuft es also ;-)

Viele Grüße.
 
Du hast im awk-Skript die Klammern { und } um die for-loop entfernt; das kann so nicht laufen! Abändern wie folgt:
Code:
        awk '{
                for (i = 1; i <= NF/2; i++) {
                        j = i + NF/2;
                        printf "%e%s", $i + $j, i < NF/2 ? "\t" : "\n";
                }
        }' > $OUT/$B
und nochmal versuchen ...
 
Zuletzt bearbeitet:
Oh mann... bin ich ein Blindfisch. Es klappt jetzt. Danke!

Gruss.
 
Zurück
Oben