Liste mit Objekten in Schleife befühlen

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
python_student
User
Beiträge: 23
Registriert: Samstag 22. Februar 2020, 13:02

Hallo,
kurz zur meiner Lage. Ich habe ein Dataframe, welches in einer Klasse zugeordnet werden muss. Mit einer Schleife gehe ich Zeilenweise in einer Spalte durch und füge jeweils den string in das Attribut des Objekts mit einer temp. Variable. Im Anschluss befühle ich eine Liste die vor der Schleife definiert wird, mit der Funktion append von der temp. Variable in der Schleife.
Leider funktioniert das nicht.
Erster durchlauf in der Schleife ist noch alles ok. Sobald aber der nächste Durchlauf kommt, wird jedes Objekt in der Liste überschrieben mit dem neuen Objekt und die liste wird um ein Objekt länger.
Ich entschuldige mich im Voraus wenn dieser Beitrag schon existent ist und ich einfach zu doof war genauer zu suchen. Leider bin ich noch ziemlich neu in Python, bitte habt Verständnis.
Hier ein Codeausschnitt:

liste = []

class record:
name = ""
provider = ""
ram = 0
rom = 0
cpu_load = 0.0

...# hier wird Variable excel_file mit Dataframe initialisiert.

temp = record

i = 0
for i in range(len(excel_file)):
temp.name = excel_file['Name']
liste.append(temp)


Vielen Dank schon mal vorab.
Benutzeravatar
__blackjack__
User
Beiträge: 13099
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@python_student: Du hast da eine Klasse `record` und definierst Attribute auf *der Klasse*! Da gehören die aber nicht hin, denn auf der Klasse definierte Attribute gelten für alle Objekte von diesem Typ, das heisst eine Veränderung ist bei allen `record`-Exemplaren sichtbar. Das sind im Grunde globale Variablen, und davon sollte man die Finger lassen.

Das nächste Problem ist das Du nie tatsächlich Objekte vom Typ `record` erzeugst. ``temp = record`` erstellt kein neues Objekt sondern Du hast den Typ `record` jetzt zusätzlich an den Namen `temp` gebunden. Was auch immer das für einen Sinn haben sollte.

In der Python-Dokumentation gibt es ein Tutorial, das solltest Du mal durcharbeiten für die Grundlagen.

Ich benutze ganz gerne das externe `attr`-Modul um einfache Klassen zu schreiben und da gleich ein bisschen sinnvolles Grundverhalten automagisch definiert zu bekommen.

``i = 0`` ist sinnfrei weil dieser Wert ja nie irgendwo verwendet wird.

``for i in range(len(sequence)):`` nur um `i` dann als Index zu verwenden ist in Python ein „anti pattern“. Du suchst die `iterrows()`-Methode von `DataFrame` oder `itertuples()`. Wenn Du da vorher die Spalten umbenennst, kannst Du Dir viel Arbeit sparen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
einfachTobi
User
Beiträge: 491
Registriert: Mittwoch 13. November 2019, 08:38

Und auch da gilt wieder: Wenn man über ein Array oder DataFrame iteriert, macht man in der Regel etwas falsch. Wenn du uns mehr Drumherum und Code gibst, können wir vielleicht noch eine einfachere Variante finden :)
python_student
User
Beiträge: 23
Registriert: Samstag 22. Februar 2020, 13:02

In der Python-Dokumentation gibt es ein Tutorial, das solltest Du mal durcharbeiten für die Grundlagen.
Ja da bin ich voll und ganz bei dir. Wollte eigentlich nicht so weit ausholen aber muss das jetzt tun.
Ich hatte in meinem Studium nur was mit c, c++ und Vhdl zu tun. Seid kurzer Zeit bin ich als Werkstudent angestellt. Dort wurde mir das erste Projekt geschenkt. In Kurzform: Ich soll mit irgend einer Programmiersprache zB. Python ein Programm schreiben, welches cdv Daten und xlsx Parsen kann. Um alles aufzuzählen was das Programm tun soll würde hier den Rahmen sprengen. In erster Linie sollte das Programm die einzelnen Spalten in eine Klasse zwischen Speichern um danach aus dieser Liste die jeweiligen Attribute, im neuen sheet, mit einer anderen Struktur wieder aufzubauen. Das mit der eigenen Klasse habe ich mir als Lösungsweg vorgenommen. Um nochmal kurz auf das Thema "Tutorial und Dokumentation zurück zu kommen". Ich bin momentan mit der Aufgabenstellung total überfordert. Ich versuche in der Arbeit wenigstens Codeabschnitte hinzuzufügen um nicht einen kompletten blanken Editor am Monitor zu haben. Nach der Arbeit sitze ich bis in die Nacht und gehe Tutorials von Python durch. Deswegen kann ich euch schon mal vorab ein FETTES Dankeschön sagen!!!!
Du hast da eine Klasse `record` und definierst Attribute auf *der Klasse*! Da gehören die aber nicht hin, denn auf der Klasse definierte Attribute gelten für alle Objekte von diesem Typ, das heisst eine Veränderung ist bei allen `record`-Exemplaren sichtbar. Das sind im Grunde globale Variablen, und davon sollte man die Finger lassen.
Leider sind die Attribute beim Kopieren nicht eingerückt. Hoffe habe das jetzt richtig interpretiert.
Das nächste Problem ist das Du nie tatsächlich Objekte vom Typ `record` erzeugst. ``temp = record`` erstellt kein neues Objekt sondern Du hast den Typ `record` jetzt zusätzlich an den Namen `temp` gebunden. Was auch immer das für einen Sinn haben sollte.
Das versteh ich noch nicht ganz. Woher weis Python das ich eine Variable als Record Objekt erstellen will. Oder besser gesagt wie macht man es anders.
Ich benutze ganz gerne das externe `attr`-Modul um einfache Klassen zu schreiben und da gleich ein bisschen sinnvolles Grundverhalten automagisch definiert zu bekommen.

Ok das konnte ich umsetzen siehe code unten. Hoffentlich auch einigermaßen richtig.
``for i in range(len(sequence)):`` nur um `i` dann als Index zu verwenden ist in Python ein „anti pattern“. Du suchst die `iterrows()`-Methode von `DataFrame` oder `itertuples()`. Wenn Du da vorher die Spalten umbenennst, kannst Du Dir viel Arbeit sparen.
Auch das habe ich versucht umzusetzen. Aber jetzt passiert was magisches in Kombination mit attr.ib, was ich noch nicht verstehe und auch durch google nicht schlau werde. Es wird eine series auf die liste angelegt und sofort ohne der Schleife alles initialisiert. Deswegen habe ich die Schleife erst einmal weggemacht damit dies nicht mehrmals passiert.
Dadurch bekomme ich aber ein weiteres Problem ich habe in einer Spalte im alten sheet die Rom Daten immer gefolgt mit ram Daten. Diese müssen im neuen sheet jeweils im passenden Namen in der gleichen Zeile stehen. Ich kann aber jetzt nicht mehr mit einer Laufvariablen i und i+1 wählen hmmmm.... Sorry wenn ich in Python so unbewandert bin. Ich hoffe ihr habt Verständnis.
python_student
User
Beiträge: 23
Registriert: Samstag 22. Februar 2020, 13:02

hier der code:

import pandas as pd
import attr
from pandas import DataFrame

liste = []

@attr.s

class record(object):# automagisch
name = attr.ib()
provider = attr.ib()
ram = attr.ib()
rom = attr.ib()
cpu_load = attr.ib()

excel_file = pd.read_excel("tabelleneu2_vereinfacht.xlsx") # read a xlsx data
excel_file: DataFrame = pd.DataFrame(excel_file)

excel_file["is_duplicate"] = excel_file[
'Name'].duplicated() # fügt eine neue Spalte hinzu als duplicate und trägt die doppelten als boolischen wert ein

temp = record

#for index, row in excel_file.iterrows():
temp.name = excel_file['Name']
temp.ram = excel_file['current']
temp.provider = excel_file['provider']
temp.rom = excel_file['current']
liste.append(temp)


i = 0
n = len(liste)# ich bin mir sicher auch an dieser schleife gibt es was zu meckern aber lassen wir das mal bitte aussen vor
while i < n:
print(liste.name)
print(liste.provider)
i = i + 1
Sirius3
User
Beiträge: 17745
Registriert: Sonntag 21. Oktober 2012, 17:20

Wenn du die absoluten Grundlagen von Python nicht kennst, dann kann man Dir schlecht helfen.
In C++ wird ja auch nicht mit temp=record eine Instanz der Klasse erzeugt.
So wie Du die Aufgabe beschreibst, brauchst Du wahrscheinlich eh keine eigene Klasse, sondern musst nur etwas mir Pandas DataFrame herumspielen.
Benutzeravatar
__blackjack__
User
Beiträge: 13099
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ansonsten wie gesagt, der einfachste und wahrscheinlich auch effizienteste Weg die Spalten die Du haben willst aus dem DataFrame selectieren, die Spaltennamen so ändern das sie den Attributnamen entsprechen und dann `itertuples()` auf dem `DataFrame` verwenden um die einzelnen Datensätze als `collections.namedtuples` (oder was vergleichbares) zu bekommen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
python_student
User
Beiträge: 23
Registriert: Samstag 22. Februar 2020, 13:02

Danke euch für die Unterstützung und danke dass ihr mich zu meinen anfänglichen Weg weist. Ich bin nur vom Pfad abgekommen weil es Leute gab die gesagt haben mit oop komme ich bei dieser Aufgabe weiter.
Ich habe heute ein wenig meine Hausaufgaben gemacht zum Thema Dataframe und meine Daten etwas hin und her geschubst wie ich es brauche. Es ist noch lang nicht fertig aber ich möchte wissen ob dies eher in die richtige Richtung geht und ob ich noch irgendwo gravierende Fehler mache?
Neuer Codeansatz:

import pandas as pd
from pandas import DataFrame

#(NUR XLSX DATEI)aus welcher Datei soll gelesen werden(entweder Pfad oder den Dateinamen eingeben wenn diese sich im gleichen Ordner befindet
file_xlsx = 'tabelleneu2_vereinfacht.xlsx'

#(NUR CSV DATEI)aus welcher Datei soll gelesen werden(entweder Pfad oder den Dateinamen eingeben wenn diese sich im gleichen Ordner befindet
file_csv = 'tabelleneu_csv.csv'# cdv Daten werden im Moment nur gelesen


#Überschriften in Spalten altes_sheet
#Spalte 1 und 2 sind gleich deswegen hier nicht aufgeführt
col3_file = 'speicher'
col4_file = 'current'

#Überschriften in Spalten neues_sheet
col1 = 'Name'
col2 = 'provider'
col3 = 'ram'
col4 = 'rom'


excel_file = pd.read_excel(file_xlsx) # read a xlsx data
excel_file: DataFrame = pd.DataFrame(excel_file) # create a dataframe

csv_file = pd.read_csv( file_csv ,sep=";",decimal=",")# read a csv data and create a dataframe

new = excel_file.copy() #copy to make edits

print(excel_file)

new = new.drop(new.columns[[2, 3]], axis='columns') # Spalte 2 'speicher' und 3 'current werden gelöscht


new[col3] = 1 # Spalte 2 wird mit ram initialisiert
new[col4] = 1# Spalte 3 wird mit rom initialisiert

excel_file["is_duplicate"] = excel_file[
col1 ].duplicated() # fügt eine neue Spalte hinzu als duplicate

for index, row in excel_file.iterrows():
if excel_file[col3_file][index] == str(col3): #Vergleicht ob er in der 'ram' Zeile ist
new.loc[index, col3] = excel_file[col4_file][index] #schreibt den wert von current in die neue 'ram spalte


if excel_file["is_duplicate"][index]:
if excel_file[col3_file][index] == str(col4): #Vergleicht ob er in der 'rom' Zeile ist
new.loc[index-1, col4] = excel_file[col4_file][index] #schreibt den wert von current in die neue 'rom' spalte

new["is_duplicate"] = new[
col1].duplicated() # fügt eine neue Spalte hinzu als duplicate

for index, row in new.iterrows():# löscht doppelte zeilen
if new["is_duplicate"][index] == True:
new.drop([index], inplace=True)

new = new.drop(new.columns[[4]], axis='columns')# Hilfsspalte is duplicate wird gelöscht
Benutzeravatar
__blackjack__
User
Beiträge: 13099
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@python_student: Wenn Du ``for``-Schleifen über `DataFrame`-Objekte schreibst, dann machst Du mit 98%iger Sicherheit etwas falsch.

Du importiest `pandas` als `pd` und noch mal `DataFrame` aus dem `pandas`-Modul und verwendest `DataFrame` dann sowohl direkt als auch über `pd`. Warum? Wonach entscheidest Du ob Du `DataFrame` direkt verwendest oder über `pd.DataFrame` ansprichst? Das ist verwirrend.

`file_xlsx` und `file_csv` sind Datei*namen* und keine Dateiobekte, die Namen sind also irreführend. `filename_xlsx` wäre treffend aber solange Du nicht Yoda bist müsste das wohl eher `xlsx_filename` heissen. Analog dazu `csv_filename`.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Konstanten werden per Konvention KOMPLETT_GROSS geschrieben.

`xlsx_file` und `csv_file` sind als Namen falsch weil da gar keine Dateiobjekte dran gebunden werden, sondern `DataFrame`-Objekte. `csv_file` wird zudem nirgends verwendet. Womit auch `csv_filename` bzw. `CSV_FILENAME` überflüssig wird.

`pandas.read_excel()` liefert einen `DataFrame`. Daraus dann einen `DataFrame` zu machen ist sinnfrei – das ist bereits einer.

Welches Programm verwendest Du zum überprüfen von Typannotationen? Denn nur dann macht das (vielleicht) Sinn die zu schreiben.

Was da mit dem/den `DataFrame`(s) gemacht wird finde ich recht undurchsichtig, ich bin mir aber fast sicher dass das ohne `new` (ist auch ein sehr schlechter Name) und ohne ``for``-Schleifen geht. Die ``for``-Schleife in der Zeilen gelöscht werden ist zudem fehlerhaft weil da Zeilen gelöscht werden während darüber iteriert wird, was dazu führt, dass Zeilen übersprungen werden. Selbst wenn das nicht fehlerhaft wäre, ist es extrem ineffizient das so zu machen.

Statt Spalten zu löschen würde ich in der Regel eher die Spalten selektieren die man behalten möchte. Das ist Robuster wenn in den Eingabedaten Spalten hinzukommen und/oder sich dort die Spaltenreihenfolge ändert. Auch beim löschen von Zeilen würde man eher selektieren was man behalten möchte.

`col3` und `col4` sind Zeichenketten. Es macht keinen Sinn `str()` damit aufzurufen, dadurch werden die nicht noch ”zeichenkettiger” oder so.

Die durchnummerierten Namen sind schlecht da nichtssagend. Noch schlechter sind die magischen Spaltennummern die dann teilweise in Kommentaren erklärt werden. Wenn die Namen haben, sollte man die Namen verwenden (und hoffen das die Aussagekräftig sind).
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

@python_student: Code hier im Forum solltest du in Code Tags schreiben, damit die Einrückung erhalten bleibt. Die werden automatisch eingefügt, wenn du in "Vollständiger Editor & Vorschau" auf den </> Button drückst.
einfachTobi
User
Beiträge: 491
Registriert: Mittwoch 13. November 2019, 08:38

Neben den ganzen formalen Anmerkungen von blackjack, passiert da viel komisches Zeug. Spaltennamen in Variablen abzulegen ist merkwürdig, denn dann kannst du gleich den Spaltennamen verwenden. Hat es einen Grund, dass du eine Kopie erstellst, welche du dann bearbeitest? Du kannst doch das eigentliche DataFrame bearbeiten. Warum wird die Spalte `is_duplicated` hinzugefügt und dann wieder gelöscht? Wenn du die nur zum aussortieren brauchst, genügt es alle Daten auszuwählen, welche eben kein Duplikat sind bzw. die Duplikate zu löschen:

Code: Alles auswählen

excel_df = excel_df[~excel_df['Name'].duplicated()]
#oder 
excel_df = excel_df.drop_duplicates(subset='Name', ignore_index=True)
.
Genau so bzw. so ähnlich kannst du bei deinen anderen Filterungen verfahren. Wenn du uns einen Beispieldatensatz gibst und deine erwartete Ausgabe bzw. Regeln zur Erstellung eben dieser, können wir sicher eine schlanke Methode vorschlagen. Denn dein jetziges Vorgehen zum Durchlaufen der DataFrames ist ziemlich umständlich.
python_student hat geschrieben:Um alles aufzuzählen was das Programm tun soll würde hier den Rahmen sprengen.
Diese Mühe solltest du dir allerdings machen. Ich habe die Vermutung, dass du dein Vorhaben falsch angehst, weil du die Möglichkeiten einfach noch nicht kennst.

PS: Was hat das mit Datenbankprogrammierung zu tun, sodass es in diesem Forum ist?
python_student
User
Beiträge: 23
Registriert: Samstag 22. Februar 2020, 13:02

Ok vielen Dank für die Hilfe. Das Thema passt auch nicht mehr zusammen. Werde ein anderes Thema erföffnen und fragen dazu stellen
Antworten