Suche Ansatz: Flächen abhängig von anderen Attribut summiere

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

Mittwoch 14. November 2007, 11:19

Mir fehlt gerade total ein passender Ansatz

Ich habe eine Tabelle, darin stehen u.a. drin Flächen und ein jewiliges Datum, wann die Fläche aufgenommen wurde: (so ungefähr)

Name Fläche Datum
x 23 1996
x 4 2000
x 56 2000
y 7 1998
y 12 1998

Ich brauche am Ende das Datum, was den größte Flächenanteil einnimmt für den jeweiligen Namen.

Hatte überlegt, einfach die Flächen aufzusummieren, wenn das Datum gleich ist, aber ich weiß vorher nicht, wieviele Einträge ich überhaupt vergleichen muss, das ist unterschiedlich. Ich kann auch nicht so ganz beliebig durch die Tabelle hin und herspringen.

Hatte überlegt, dass so lange Name x ich Fläche und Datum in eine List, bzw dict scheibe und dann gucke, aber ich weiß da gerade nicht so recht weiter.

Hat wer eine Idee, wie ich das am besten machen könnte?
BlackJack

Mittwoch 14. November 2007, 12:19

Mal sehen ob ich's richtig verstanden habe: Aus den Daten willst alle Flächenwerte mit gleichem Namen und gleicher Jahreszahl aufaddieren und dann pro Namen jeweils das Jahr mit der grössten Fläche wissen!?

Also aufaddieren im Beispiel:

Name Fläche Datum
x 23 1996
x 60 2000
y 19 1998

Und Endergebnis dann:

x 2000
y 1998

Wenn die erste Zeile der Ausgangsdaten 'x 58 1996' lauten würde, wäre trotzdem 'x 2000' die richtige Antwort?

Kann man irgendwelche Annahmen über die Reihenfolge der Ausgangsdaten machen? In Deinem Beispiel sind die ja nach Namen und dann nach Jahr sortiert. Zufall?

Für den unsortierten Fall ist das hier ein Lösungsansatz:

Code: Alles auswählen

from collections import defaultdict


def aggregate_years(table):
    result = defaultdict(int)
    for name, area, year in table:
        result[name, year] += area
    return ((name, area, year) for (name, year), area in result.iteritems())


def largest_areas(table):
    result = defaultdict(lambda: (0, 0))
    for name, area, year in aggregate_years(table):
        result[name] = max(result[name], (area, year))
    return dict((name, year) for (name, (area, year)) in result.iteritems())


def main():
    table = (('x', 23, 1996),
             ('x', 4, 2000),
             ('x', 56, 2000),
             ('y', 7, 1998),
             ('y', 12, 1998))
    print largest_areas(table)  # => {'y': 1998, 'x': 2000}
Wenn die Daten aus der Tabelle nach Namen oder nach Namen und Jahr sortiert sind, geht's ein wenig effizienter.
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

Mittwoch 14. November 2007, 12:43

BlackJack hat geschrieben:Mal sehen ob ich's richtig verstanden habe: Aus den Daten willst alle Flächenwerte mit gleichem Namen und gleicher Jahreszahl aufaddieren und dann pro Namen jeweils das Jahr mit der grössten Fläche wissen!?

Also aufaddieren im Beispiel:

Name Fläche Datum
x 23 1996
x 60 2000
y 19 1998

Und Endergebnis dann:

x 2000
y 1998

Wenn die erste Zeile der Ausgangsdaten 'x 58 1996' lauten würde, wäre trotzdem 'x 2000' die richtige Antwort?

Kann man irgendwelche Annahmen über die Reihenfolge der Ausgangsdaten machen? In Deinem Beispiel sind die ja nach Namen und dann nach Jahr sortiert. Zufall?

Wenn die Daten aus der Tabelle nach Namen oder nach Namen und Jahr sortiert sind, geht's ein wenig effizienter.
Also die Daten sind nach Namen sortiert. Und der ist für mich nicht relevant, weil sobald sich der Name ändert, kommt der berechnete Wert in meine Datenbank.
Tabelle liegt in ner Geodatabase und ich komm da nicht ganz richtig ran. Das Beispiel war nur zur Veranschaulichung. Die sind NICHT nach Jahr, sondern nur nach Name sortiert. Das Beispiel hier ist jetzt nur konstruiert. ... :)
Also ich habe immer nur einen Namen auf einmal zu handlen, dann ist gut.
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

Mittwoch 14. November 2007, 13:09

defaultdict geht auch nur mit 2.5, oder? Ich bin wegen ArcGis an 2.4 gebunden :(
BlackJack

Mittwoch 14. November 2007, 13:22

Okay, das ganze ohne `defaultdict`:

Code: Alles auswählen

def aggregate_years(table):
    result = dict()
    for name, area, year in table:
        key = (name, year)
        result[key] = result.get(key, 0) + area
    return ((name, area, year) for (name, year), area in result.iteritems())


def largest_areas(table):
    result = dict()
    for name, area, year in aggregate_years(table):
        result[name] = max(result.get(name, (0, 0)), (area, year))
    return dict((name, year) for (name, (area, year)) in result.iteritems())


def main():
    table = (('x', 23, 1996),
             ('x', 4, 2000),
             ('x', 56, 2000),
             ('y', 7, 1998),
             ('y', 12, 1998))
    print largest_areas(table)  # => {'y': 1998, 'x': 2000}
BlackJack

Mittwoch 14. November 2007, 13:37

Und jetzt etwas effizienter für sortierte Namen:

Code: Alles auswählen

from itertools import groupby
from operator import itemgetter

fst = itemgetter(0)
snd = itemgetter(1)


def max_area_year(table):
    result = dict()
    for dummy, area, year in table:
        result[year] = result.get(year, 0) + area
    return snd(max((area, year) for year, area in result.iteritems()))


def largest_areas(table):
    return ((name, max_area_year(group)) for name, group in groupby(table, fst))


def main():
    table = (('x', 23, 1996),
             ('x', 4, 2000),
             ('x', 56, 2000),
             ('y', 7, 1998),
             ('y', 12, 1998))
    print list(largest_areas(table))  # => [('x', 2000), ('y', 1998)]
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

Samstag 17. November 2007, 10:18

okay, also wenn ich das richtig verstanden habe:

groupby fasst gleiche Listeneinträge zusammen, für einen Vorgegebenen Key:

Code: Alles auswählen

In [32]: for group in groupby(table, snd):
   ....:     print group
   ....:
   ....:
(1996, <itertools._grouper object at 0x00A9D0C0>)
(2000, <itertools._grouper object at 0x00A9D120>)
(1998, <itertools._grouper object at 0x00A9D0C0>)

Praktisch in dem Fall für das Jahr, weil ich das als Key festgelegt habe (mit dem snd). Und das 1. Argument gibt an, welche Liste gruppiert werden soll?
BlackJack

Samstag 17. November 2007, 10:46

Fast richtig. Es werden gleiche ("gleich" nach Schlüsselfunktion) *aufeinanderfolgende* Elemente zusammengefasst. Wenn die Ausgangsdaten nicht nach Jahr sortiert sind kann das gleiche Jahr öfter vorkommen. Darum fragte ich ja weiter oben wie die Daten sortiert sind.
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

Samstag 17. November 2007, 11:08

achso .... :) okay...

ABER, wenn ich vorher schon mit einer for schleife durch meine Tabelle laufe und dann mit Hilfe von takewhile und dropwhile am Ende eh nur die Fälle habe, wo name = (also z.B name NUR x) .... dann brauch ich ja theoretisch nur die 1. Funktion max_area_year.

(Sorry, wie gesagt, ich lerne das alles noch und brauch wahrscheinlich einen Moment länger, bevor es vollkommen klick macht)

Code: Alles auswählen

for dummy, area, year in table:
        result[year] = result.get(year, 0) + area
ich hab praktisch ein neues leeres dictionary angelegt, was da heißt result, in das ich nun für jeden Listeneintrag in table einen Eintrag mache, Schlüssel lege ich year fest und da es pro Jahr mehrere Einträge gibt, wird für einen gleichen Year eintrag die Fläche einach aufsummiert?
BlackJack

Samstag 17. November 2007, 11:11

Das sollte so funktionieren, ja.
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

Samstag 17. November 2007, 13:44

wenn ich das einzeln auseinander nehmen (also versuche gerade das im Detail alles einzeln zu verstehen, was was zurückgibt):

Code: Alles auswählen

In [108]: year, area in result.iteritems()
Out[108]: (2000, False)
warum wird da ein False ausgegeben,

Code: Alles auswählen

In [109]: for year, area in result.iteritems():
   .....:     print year, area
   .....:
   .....:
2000 6790.5
2001 6789.67
hier aber nicht?
Benutzeravatar
beewee
User
Beiträge: 35
Registriert: Mittwoch 18. Januar 2006, 22:16

Samstag 17. November 2007, 14:09

meneliel hat geschrieben:

Code: Alles auswählen

In [108]: year, area in result.iteritems()
Out[108]: (2000, False)
warum wird da ein False ausgegeben,

Code: Alles auswählen

In [109]: for year, area in result.iteritems():
   .....:     print year, area
   .....:
   .....:
2000 6790.5
2001 6789.67
hier aber nicht?
Weil

Code: Alles auswählen

year, area in result.iteritems()
äquivalent zu

Code: Alles auswählen

(year, (area in result.iteritems())
ist, während

Code: Alles auswählen

for year, area in result.iteritems():
das Gleiche wie

Code: Alles auswählen

for (year, area) in result.iteritems():
ist.

BeeWee
Antworten