iterrows über mehrere dataframes per loop

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

@ChrisLi: Dann bring deine Daten bzw. Dataframe in Ordnung.
Irgendo wird der ja erstellt. Ich hoffe aus einem Haufen Daten. Und Pandas erkennt eigentlich sehr gut den Typen der Felder. Wenn der nicht stimmt ist das ein Zeichen dafür, dass etwas mit den Daten nicht stimmt (mindestens ein Feld enthält nicht, was man denkt) oder der Datsframe wurde falsch erstellt.

Jetzt landen wir wieder bei dem Pandas Tutorial und der Frage, wie du den DF erstellst.
ChrisLi
User
Beiträge: 17
Registriert: Dienstag 26. September 2023, 23:06

Ich verbinde mit via der MT5-Api mit einem Broker, dann:

Code: Alles auswählen

rates_H1 = mt5.copy_rates_range("USTEC", mt5.TIMEFRAME_H1, utc_from, utc_to)
dann:

Code: Alles auswählen

df_H1 = pd.DataFrame(rates_H1)
Auch wenn ich :

Code: Alles auswählen

df_H1['time']=pd.to_datetime(df_H1['time'], unit='s')
bleibt es beim 'str' object..

Tutorials ja, ich bin dran.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Wenn Du diese Fehlermeldung bekommst, dann ist `df` ein String und kein Dataframe. Also machst Du noch irgendwo anders irgendetwas falsches.
Zeige also Deinen wirklichen Code inklusive komplettem Traceback.
einfachTobi
User
Beiträge: 491
Registriert: Mittwoch 13. November 2019, 08:38

Wenn du diese Fehlermeldung bei dem Code bekommst, dann sind df_M5, df_M30, df_H1 keine DataFrames.
ChrisLi
User
Beiträge: 17
Registriert: Dienstag 26. September 2023, 23:06

Sirius3 hat geschrieben: Montag 2. Oktober 2023, 20:28 Wenn Du diese Fehlermeldung bekommst, dann ist `df` ein String und kein Dataframe. Also machst Du noch irgendwo anders irgendetwas falsches.
Zeige also Deinen wirklichen Code inklusive komplettem Traceback.
Hier sieht man auch, wie ich halt gruselig mit iterrows() leere DFs befülle; jedenfalls liefert mir das die Daten, die ich sehen will.

Code: Alles auswählen


# logging into broker with credentials..
...
..
if authorized:
    account_info_dict = mt5.account_info()._asdict()
    for prop in account_info_dict:
        print("   {}={}".format(prop, account_info_dict[prop]))
else:
    print("failed to connect at account #{}, error code: {}".format(account, mt5.last_error()))


# set time zone to UTC
timezone = pytz.timezone("Etc/UTC")

# select symbol
selected=mt5.symbol_select("EURUSD",True)
if not selected:
    print("failed to select symbol")
    mt5.shutdown()
    quit()

# create 'datetime' object in UTC time zone to avoid the implementation of a local time zone offset
utc_from = datetime(2023, 1, 1, tzinfo=timezone)
utc_to = datetime(2023, 10, 3, tzinfo=timezone)

# get the bar data
rates_H1 = mt5.copy_rates_range("EURUSD", mt5.TIMEFRAME_H1, utc_from, utc_to)

# shut down connection to the MetaTrader 5 terminal
mt5.shutdown()

# put received rates into dataframe
df_H1 = pd.DataFrame(rates_H1)

# convert time in seconds into the datetime format
df_H1['time']=pd.to_datetime(df_H1['time'], unit='s')

# create new dataframes for filtered data
df_stats_H1_08 = pd.DataFrame(columns=('time', 'high08', 'low08'))
df_stats_H1_812 = pd.DataFrame(columns=('time', 'high812', 'low812'))

#

filter08 = df_H1.time.dt.hour.between(0, 8, 'both')
filter812 = df_H1.time.dt.hour.between(8, 12, 'both')
filter0 = df_H1.time.dt.hour == 0
df_H1_filtered = df_H1[["time", "high", "low", ]]

high08 = 0
low08 = 1000
for pos, d in df_H1_filtered.iterrows():
    if (d.time.hour >= 0) & (d.time.hour <= 8):
      if (d.high > high08):
        high08 = d.high
      if (d.low < low08):
        low08 = d.low
      if (d.time.hour == 8):
        df_stats_H1_08.loc[pos] = [d.time, high08, low08]
        high08 = 0
        low08 = 1000

high812 = 0
low812 = 1000
for pos, d in df_H1_filtered.iterrows():
    if (d.time.hour >= 8) & (d.time.hour <= 12):
      if (d.high > high812):
         high812 = d.high
      if (d.low < low812):
        low812 = d.low
      if (d.time.hour == 20):
        df_stats_H1_812.loc[pos] = [d.time, high812, low812]
        high812 = 0
        low812 = 1000


# resetting index that merging dfs work
df_stats_H1_08.reset_index(inplace=True)
df_stats_H1_812.reset_index(inplace=True)
print("df_stats_H1_08: \n", df_stats_H1_08)
print("df_stats_H1_812: \n", df_stats_H1_812)

# adding cols to df

df_stats_H1_08['high812'] = df_stats_H1_812['high812']
df_stats_H1_08['low812'] = df_stats_H1_812['low812']
df_stats_H1_08['high2high'] = df_stats_H1_08['high812'] - df_stats_H1_08['high08']
df_stats_H1_08['low2low'] = df_stats_H1_08['low812'] - df_stats_H1_08['low08']

print(df_H1.dtypes)

for df in df_H1:
     print(df)
     condition = (df.time.dt.hour >= 0) & (df.time.dt.hour <= 8)
     df["h2l"] = df[condition].high - df[condition].low
     print("df: \n", df)
Man sieht:

Code: Alles auswählen

Columns: [index, time, high812, low812]
Index: []
time           datetime64[ns]
open                  float64
high                  float64
low                   float64
close                 float64
tick_volume            uint64
spread                  int32
real_volume            uint64
dtype: object
dtype object, das ist kein DF, aber for df in df_H1, das ist der original befüllte df.
Benutzeravatar
__blackjack__
User
Beiträge: 13116
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Keine Ahnung was Du genau mit dem letzten Satz sagen willst, aber `df` in der letzten Schleife ist natürlich *kein* DataFrame, denn wenn man *über* einen DataFrame iteriert bekommt man als einzelne Elemente keine DataFrames. Was sollten die denn auch enthalten beziehungsweise wie sollten die aussehen?

Wie schon gesagt, man befüllt keine leeren DataFrame-Objekte Datensatz für Datensatz. Das ist falsch. Nicht nur ein bisschen unschön sondern falsch. Und ja, das funktioniert. Ich kann auch meinen Nachbarn erschiessen wenn der Fernseher zu laut ist. Das funktioniert. Ist aber mindestens genau so falsch.

Wenn `authorized` nicht zutrifft macht der Code einfach weiter als wäre nix. Ich bin mir ziemlich sicher das wird nicht funktionieren.

`quit()` gibt es eigentlich gar nicht. Die Funktion ist für den interaktiven Interpreter gedacht und hat in Programmen nix zu suchen. Falls jetzt der nächste Impuls `sys.exit()` sein sollte: Das ist dazu da um mindestens potentiell einen anderen Rückgabecode als 0 an den aufrufenden Prozess zu übermitteln. Könnte man hier vielleicht sogar sinnvoll machen.

Den `mt5.shutdown()`-Aufruf sollte man mit einem ``try``/``finally`` sicherstellen.

Insgesamt sieht das nach zu viel Code am Stück aus. Und das steht auch alles auf Modulebene. Das sollte man sinnvoll auf Funktionen aufteilen, auch um einzelne Teile leichter testen zu können. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Jetzt hast Du ja immer noch for-Schleifen obwohl Du ja schon die passenden Filter definiert hast. Warum?
Was soll auch diese komische if-Bedingungen, mit denen Du low und high auf 0 bis 1000 einschränkst? Ist das wirklich die beabsichtigte Wirkung?

Hast Du wirklich exakt gleich viele Elemente von 0 bis 8 Uhr wie von 8 bis 12 Uhr? Weil sonst funktioniert ja die Differenzbildung nicht. Und was soll das überhaupt bedeuten, einen Wert von irgendwann zwischen 0 und 8 von einem beliebigen zwischen 8 und 12 abzuziehen?

Von Deinem Code bleibt also nicht mehr viel übrig, weil man mit Pandas solche Operationen sehr kompakt schreiben kann:

Code: Alles auswählen

def read_data():
    try:
        selected=mt5.symbol_select("EURUSD",True)
        if not selected:
            raise RuntimeError("failed to select symbol")
        timezone = pytz.timezone("Etc/UTC")
        utc_from = datetime(2023, 1, 1, tzinfo=timezone)
        utc_to = datetime(2023, 10, 3, tzinfo=timezone)
        rates_H1 = mt5.copy_rates_range("EURUSD", mt5.TIMEFRAME_H1, utc_from, utc_to)
    finally:
        mt5.shutdown()
    return rates_H1


def main():
    ...
    df_H1 = pd.DataFrame(read_data())
    df_H1['time']=pd.to_datetime(df_H1['time'], unit='s')
    df_H1['low'] = df_H1['low'].clip_upper(1000)
    df_H1['high'] = df_H1['high'].clip_lower(0)

    filter08 = df_H1.time.dt.hour.between(0, 8, 'both')
    filter812 = df_H1.time.dt.hour.between(8, 12, 'both')
    df_stats_H1_08 = df_H1[filter08]
    df_stats_H1_812 = df_H1[filter812]

    df_stats_H1_08.reset_index(inplace=True)
    df_stats_H1_812.reset_index(inplace=True)

    df_stats_H1_08['high2high'] = df_stats_H1_08['high'] - df_stats_H1_812['high']
    df_stats_H1_08['low2low'] = df_stats_H1_08['low'] - df_stats_H1_812['low']
    ...


if __name__ == "__main__":
    main()
ChrisLi
User
Beiträge: 17
Registriert: Dienstag 26. September 2023, 23:06

Sirius3 hat geschrieben: Dienstag 3. Oktober 2023, 13:18 Jetzt hast Du ja immer noch for-Schleifen obwohl Du ja schon die passenden Filter definiert hast. Warum?
Was soll auch diese komische if-Bedingungen, mit denen Du low und high auf 0 bis 1000 einschränkst? Ist das wirklich die beabsichtigte Wirkung?

Hast Du wirklich exakt gleich viele Elemente von 0 bis 8 Uhr wie von 8 bis 12 Uhr? Weil sonst funktioniert ja die Differenzbildung nicht. Und was soll das überhaupt bedeuten, einen Wert von irgendwann zwischen 0 und 8 von einem beliebigen zwischen 8 und 12 abzuziehen?

Von Deinem Code bleibt also nicht mehr viel übrig, weil man mit Pandas solche Operationen sehr kompakt schreiben kann:

Code: Alles auswählen

def read_data():
    try:
        selected=mt5.symbol_select("EURUSD",True)
        if not selected:
            raise RuntimeError("failed to select symbol")
        timezone = pytz.timezone("Etc/UTC")
        utc_from = datetime(2023, 1, 1, tzinfo=timezone)
        utc_to = datetime(2023, 10, 3, tzinfo=timezone)
        rates_H1 = mt5.copy_rates_range("EURUSD", mt5.TIMEFRAME_H1, utc_from, utc_to)
    finally:
        mt5.shutdown()
    return rates_H1


def main():
    ...
    df_H1 = pd.DataFrame(read_data())
    df_H1['time']=pd.to_datetime(df_H1['time'], unit='s')
    df_H1['low'] = df_H1['low'].clip_upper(1000)
    df_H1['high'] = df_H1['high'].clip_lower(0)

    filter08 = df_H1.time.dt.hour.between(0, 8, 'both')
    filter812 = df_H1.time.dt.hour.between(8, 12, 'both')
    df_stats_H1_08 = df_H1[filter08]
    df_stats_H1_812 = df_H1[filter812]

    df_stats_H1_08.reset_index(inplace=True)
    df_stats_H1_812.reset_index(inplace=True)

    df_stats_H1_08['high2high'] = df_stats_H1_08['high'] - df_stats_H1_812['high']
    df_stats_H1_08['low2low'] = df_stats_H1_08['low'] - df_stats_H1_812['low']
    ...


if __name__ == "__main__":
    main()
Das sieht gut aus, teste ich später.
Ich schränke low und high nicht auf 0 und 1000 ein, das sind lediglich Werte, um sicherzustellen, dass die if conditions ziehen, weil die Kurse werden >0 und <1000 sein.
Ja, es werden exakt gleich viel Elemente sein, für jede Stunde genau eins; meine Differenzbildung funktioniert ja.
Mit dem Abziehen des einen Werts vom anderen überprüfe ich, ob in den Stunden von 8-12 der Kurs zum Hoch der Stunden von 0-8 zurück kam.

Das versuche ich gerade ungefähr so zu checken, dass mir hierzu "ja" und "nein" in eine weitere Spalte "back2high08" geschrieben wird:

Code: Alles auswählen

df_stats_H1_08['back2high08'] = df_stats_H1_08[['high08', 'low812']].apply(lambda x: 'ja' if df_stats_H1_08['low812'] <= df_stats_H1_08['high08'] else 'nein')
Aber das will noch nicht..
Ich finde keine Beispiele für eine lambda Funktion mit einer Condition, die abhängig von SpaltenValues ist.
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

ChrisLi hat geschrieben: Dienstag 3. Oktober 2023, 16:35 Ja, es werden exakt gleich viel Elemente sein, für jede Stunde genau eins;
Ohne jetzt tiefer eingestiegen zu sein:
1 Element je Stunde von 0-8 sind aber eine andere Anzahl von Elementen als 1 Element je Stunde von 8-12.
ChrisLi
User
Beiträge: 17
Registriert: Dienstag 26. September 2023, 23:06

sparrow hat geschrieben: Dienstag 3. Oktober 2023, 16:40
ChrisLi hat geschrieben: Dienstag 3. Oktober 2023, 16:35 Ja, es werden exakt gleich viel Elemente sein, für jede Stunde genau eins;
Ohne jetzt tiefer eingestiegen zu sein:
1 Element je Stunde von 0-8 sind aber eine andere Anzahl von Elementen als 1 Element je Stunde von 8-12.
Das ist ein berechtigter Einwand und jetzt mach ich erstmal Pause =)
ChrisLi
User
Beiträge: 17
Registriert: Dienstag 26. September 2023, 23:06

ChrisLi hat geschrieben: Dienstag 3. Oktober 2023, 16:35
Sirius3 hat geschrieben: Dienstag 3. Oktober 2023, 13:18 Jetzt hast Du ja immer noch for-Schleifen obwohl Du ja schon die passenden Filter definiert hast. Warum?
Was soll auch diese komische if-Bedingungen, mit denen Du low und high auf 0 bis 1000 einschränkst? Ist das wirklich die beabsichtigte Wirkung?

Hast Du wirklich exakt gleich viele Elemente von 0 bis 8 Uhr wie von 8 bis 12 Uhr? Weil sonst funktioniert ja die Differenzbildung nicht. Und was soll das überhaupt bedeuten, einen Wert von irgendwann zwischen 0 und 8 von einem beliebigen zwischen 8 und 12 abzuziehen?

Von Deinem Code bleibt also nicht mehr viel übrig, weil man mit Pandas solche Operationen sehr kompakt schreiben kann:

Code: Alles auswählen

def read_data():
    try:
        selected=mt5.symbol_select("EURUSD",True)
        if not selected:
            raise RuntimeError("failed to select symbol")
        timezone = pytz.timezone("Etc/UTC")
        utc_from = datetime(2023, 1, 1, tzinfo=timezone)
        utc_to = datetime(2023, 10, 3, tzinfo=timezone)
        rates_H1 = mt5.copy_rates_range("EURUSD", mt5.TIMEFRAME_H1, utc_from, utc_to)
    finally:
        mt5.shutdown()
    return rates_H1


def main():
    ...
    df_H1 = pd.DataFrame(read_data())
    df_H1['time']=pd.to_datetime(df_H1['time'], unit='s')
    df_H1['low'] = df_H1['low'].clip_upper(1000)
    df_H1['high'] = df_H1['high'].clip_lower(0)

    filter08 = df_H1.time.dt.hour.between(0, 8, 'both')
    filter812 = df_H1.time.dt.hour.between(8, 12, 'both')
    df_stats_H1_08 = df_H1[filter08]
    df_stats_H1_812 = df_H1[filter812]

    df_stats_H1_08.reset_index(inplace=True)
    df_stats_H1_812.reset_index(inplace=True)

    df_stats_H1_08['high2high'] = df_stats_H1_08['high'] - df_stats_H1_812['high']
    df_stats_H1_08['low2low'] = df_stats_H1_08['low'] - df_stats_H1_812['low']
    ...


if __name__ == "__main__":
    main()
Das sieht gut aus, teste ich später.
Ich schränke low und high nicht auf 0 und 1000 ein, das sind lediglich Werte, um sicherzustellen, dass die if conditions ziehen, weil die Kurse werden >0 und <1000 sein.
Ja, es werden exakt gleich viel Elemente sein, für jede Stunde genau eins; meine Differenzbildung funktioniert ja.
Mit dem Abziehen des einen Werts vom anderen überprüfe ich, ob in den Stunden von 8-12 der Kurs zum Hoch der Stunden von 0-8 zurück kam.

Das versuche ich gerade ungefähr so zu checken, dass mir hierzu "ja" und "nein" in eine weitere Spalte "back2high08" geschrieben wird:

Code: Alles auswählen

df_stats_H1_08['back2high08'] = df_stats_H1_08[['high08', 'low812']].apply(lambda x: 'ja' if df_stats_H1_08['low812'] <= df_stats_H1_08['high08'] else 'nein')
Aber das will noch nicht..
Ich finde keine Beispiele für eine lambda Funktion mit einer Condition, die abhängig von SpaltenValues ist.
Update: Die Funktion habe ich hinbekommen:

Code: Alles auswählen

df_stats_H1_08['back2high08'] = df_stats_H1_08[['high08', 'low812']].apply(lambda row: True if (row['low812'] <= row['high08']) else False, axis=1)
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Oder einfach:

Code: Alles auswählen

df_stats_H1_08['back2high08'] = df_stats_H1_08['low812'] <= df_stats_H1_08['high08']
ChrisLi
User
Beiträge: 17
Registriert: Dienstag 26. September 2023, 23:06

ChrisLi hat geschrieben: Dienstag 3. Oktober 2023, 16:35
Sirius3 hat geschrieben: Dienstag 3. Oktober 2023, 13:18 Jetzt hast Du ja immer noch for-Schleifen obwohl Du ja schon die passenden Filter definiert hast. Warum?
Was soll auch diese komische if-Bedingungen, mit denen Du low und high auf 0 bis 1000 einschränkst? Ist das wirklich die beabsichtigte Wirkung?

Hast Du wirklich exakt gleich viele Elemente von 0 bis 8 Uhr wie von 8 bis 12 Uhr? Weil sonst funktioniert ja die Differenzbildung nicht. Und was soll das überhaupt bedeuten, einen Wert von irgendwann zwischen 0 und 8 von einem beliebigen zwischen 8 und 12 abzuziehen?

Von Deinem Code bleibt also nicht mehr viel übrig, weil man mit Pandas solche Operationen sehr kompakt schreiben kann:

Code: Alles auswählen

def read_data():
    try:
        selected=mt5.symbol_select("EURUSD",True)
        if not selected:
            raise RuntimeError("failed to select symbol")
        timezone = pytz.timezone("Etc/UTC")
        utc_from = datetime(2023, 1, 1, tzinfo=timezone)
        utc_to = datetime(2023, 10, 3, tzinfo=timezone)
        rates_H1 = mt5.copy_rates_range("EURUSD", mt5.TIMEFRAME_H1, utc_from, utc_to)
    finally:
        mt5.shutdown()
    return rates_H1


def main():
    ...
    df_H1 = pd.DataFrame(read_data())
    df_H1['time']=pd.to_datetime(df_H1['time'], unit='s')
    df_H1['low'] = df_H1['low'].clip_upper(1000)
    df_H1['high'] = df_H1['high'].clip_lower(0)

    filter08 = df_H1.time.dt.hour.between(0, 8, 'both')
    filter812 = df_H1.time.dt.hour.between(8, 12, 'both')
    df_stats_H1_08 = df_H1[filter08]
    df_stats_H1_812 = df_H1[filter812]

    df_stats_H1_08.reset_index(inplace=True)
    df_stats_H1_812.reset_index(inplace=True)

    df_stats_H1_08['high2high'] = df_stats_H1_08['high'] - df_stats_H1_812['high']
    df_stats_H1_08['low2low'] = df_stats_H1_08['low'] - df_stats_H1_812['low']
    ...


if __name__ == "__main__":
    main()
Das mit dem clip_upper/lower ist zu früh angewendet; es gilt (wenn es überhaupt das richtige ist..) für die Werte, die nach filter08 und filter812 übrigbleiben; aber das mag hinfällig sein, wenn folgendes ggf. ohne möglich ist, weil die 0 und die 1000 brauchte ich für meinen Schleifenunsinn, mit dem ich nun ans Ende der Möglichkeiten gekommen bin.

Es geht um die Werte, die der filter812 filtert. In den Stunden 8 - 12:oo haben wir ein high und ein low, die brauche ich; weiter müssen dann die time.hours von den beiden verglichen werden und nur die kommen durch, wo das low.time.hour > high.time.hour liegt, wo das low also nach dem high kam.

Nochmal in kurz:
1. high812 muss > high08 sein
2. low812 muss zeitlich nach high812 kommen (low812.time.hour > high812.time.hour)

Hier ein Bspw. wie der df aussieht:

Code: Alles auswählen

 time           open          high            low          close        vol       spread     real_vol
502 2021-02-03 01:00:00  13519.12  13520.75  13494.62  13499.88         3328     200            0
503 2021-02-03 02:00:00  13499.88  13545.12  13487.88  13539.38         4733     200            0
504 2021-02-03 03:00:00  13539.75  13540.00  13505.38  13522.62         5553     200            0
505 2021-02-03 04:00:00  13522.38  13534.88  13502.12  13523.88         4741     200            0
506 2021-02-03 05:00:00  13523.50  13525.12  13512.12  13520.25         2823     200            0
507 2021-02-03 06:00:00  13520.12  13538.62  13518.25  13529.00         2241     200            0
508 2021-02-03 07:00:00  13528.75  13558.25  13525.12  13556.62         3033     200            0
509 2021-02-03 08:00:00  13556.38  13560.38  13549.00  13558.12         2002     100            0
510 2021-02-03 09:00:00  13557.88  13591.62  13550.38  13589.12         3836     100            0
511 2021-02-03 10:00:00  13588.88  13596.00  13558.25  13561.75         7818     100            0
512 2021-02-03 11:00:00  13561.62  13566.12  13526.00  13531.62         6442     100            0
513 2021-02-03 12:00:00  13531.88  13562.88  13529.88  13554.88         5241     100            0
1. Das high08 (0-8:00) (Broker fängt um 1:oo an) ist 13560.38 und das high812 13596.00; Ist das high812 > das high08, dann weiter bei 2, sonst weg.
2. Wir haben das high812 von 13596.00 und low812 von 13526.00 ; das low kommt um 11:oo nach dem high um 10:oo, also will ich die beiden haben.
Wäre das low um 10:oo und das high um 11:oo gewesen, hätte der Filter es verwerfen sollen.
Antworten