Dataframes aus verschiedenen Prozessen zusammenfügen

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.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

LukeNukem hat geschrieben: Donnerstag 8. Juli 2021, 16:12 De facto ist es nun einmal auch so, daß wir gar nicht wissen, wie die Netzwerkanbindung des TO ist, wie die Serverseite aussieht, und, vor allem: wie die Daten aussehen. Aber das das hier:

Code: Alles auswählen

d['a']['b']['c'].get('eins')
d['a']['b']['c'].get(''zwei')
d['a']['b']['c'].get('drei')
d['a']['b']['c'].get('vier')
mehrmals dieselben Zugriffe auf dasselbe Dictionary macht, während

Code: Alles auswählen

dummy = d['a']['b']['c']
dummy.get('eins')
dummy.get(''zwei')
dummy.get('drei')
dummy.get('vier')
sich diesen Unfug spart, ist für einen Profi wie Dich sicherlich offensichtlich und dürfte mit wenigen Sekunden des Nachdenkens sogar einem Hobbyisten klar werden.
Da widerspreche ich auch nicht. Es ist allerdings kein wesentlich Faktor weil es andere Baustellen gibt der mehrere Größenordnungen langsamer sind. Wenn ich Performance optimiere fange ich nicht bei dem Teil an der sowieso schon am schnellsten sein wird.
Nun könnte man zwar vermuten, daß die zweite Variante eine neue Speicherallokation provoziert[...]
Könnte man wenn man von Python keine Ahnung hat, aber da keine Kopie angelegt wird sondern nur eine Referenz führt dass garantiert nicht zu einer Allokation.
[...], allerdings... einerseits optimiert Python hier schon ziemlich gut (warum gibt es eigentlich das Modul "copy"?) und auf der anderen Seite hat da womöglich auch das Betriebssystem bzw. dessen Speicherverwaltung noch etwas beizutragen, etwa mit Copy-On-Write und Same-Page-Merging.
Das Copy Modul existiert um Objekte zu kopieren, ist größtenteils unnötig und spielt hier keine Rolle da es nicht benutzt wird (und auch nicht werden sollte). Das Betriebssystem hat da wenig beizutragen zum einen weil diese Optimierungen den Speicherverbrauch gering halten und sich auf die Laufzeit, was ja hier das Problem ist nicht auswirken, zum anderen funktioniert Copy-on-Write jetzt eher nicht so gut wenn man ständig in irgendwelche Objekte schreibt wie man dass bei Reference Counting so macht.
Darüber hinaus, bitte verzeih' den Widerspruch: Python nutzt unter der Haube selbstverständlich die ganz normale malloc(3)-Funktion. Daß Python dessen Benutzung stark optimiert (wie gesagt, think "copy"-Modul), ist diesseits bekannt. Aber trotzdem danke für den Hinweis, der für andere Leser vielleicht wertvoll ist.
CPython hat einen eigenen Memory Manager der für Allokationen genutzt wird. Der baut auf malloc auf und somit ist tief unter der Haube irgendwo ein malloc versteckt wird aber i.d.R. nicht passieren wenn man ein Objekt erstellt. Damit unterscheidet sich Python da wesentlich von Sprachen wie C, C++ oder Rust wo man bei einer Heap Allokationen normalerweise ein malloc hat. Das ist auch essentiell für die Performance einer solchen Sprache wo man anders als in C, C++ oder Rust nahezu alles auf der Heap statt auf dem Stack allokiert. Die JVM ist hier ein besonders gutes Beispiel, da die sich gleich beim Startup eine ganze Heap allokiert um zur Laufzeit möglichst nie malloc aufzurufen.
mirko3107
User
Beiträge: 75
Registriert: Freitag 23. April 2021, 15:42

Könnt ihr euren Streit nicht per PN klären?

@Topic: Mein Dataframe wird immer noch nicht beschrieben, hab alle möglichen Optionen probiert.
mirko3107
User
Beiträge: 75
Registriert: Freitag 23. April 2021, 15:42

mirko3107 hat geschrieben: Freitag 9. Juli 2021, 13:51 Könnt ihr euren Streit nicht per PN klären?

@Topic: Mein Dataframe wird immer noch nicht beschrieben, hab alle möglichen Optionen probiert.

Code: Alles auswählen

  Ticker                         Name   Price    Change           Cap    EPS   Earnings     IV Rating    Volumen  SMA200    RSI  Stoch  52Whigh  52Wlow  Weekly
0      A     Agilent Technologies Inc  148.98  0.107505  4.539390e+10   4.14  8/17/2021  22.64    buy    1973342  122.73  65.71  92.53   149.19   88.07   False
1     AA                   Alcoa Corp   36.47 -3.823830  6.809861e+09   4.19  7/15/2021  55.85    buy    8577484   25.84  52.18  81.14    44.42   10.43    True
2    AAL  American Airlines Group Inc   21.01 -2.188079  1.347546e+10  -8.49  7/22/2021  47.77   hold   23985527   18.32  31.37  17.21    26.09   10.63    True
3    AAP     Advance Auto Parts, Inc.  208.70 -1.113480  1.365710e+10  10.91  8/17/2021  26.62    buy     540639  172.21  66.23  95.21   211.16  131.90   False
4   AAPL                    Apple Inc  142.02  1.471847  2.369973e+12   5.20  7/27/2021  26.62    buy  108181793  125.56  69.22  96.87   145.09   89.14    True
A 149.58 0.5 45.39 4.14 148813 149.67 88.07 Buy
AA 37.75 6.76 7.05 4.27 2009404 44.42 10.43 Buy
AAL 20.9 2.7 13.41 -8.54 7809996 26.09 10.63 Hold
AAP 211.57 1.96 13.84 10.92 93008 212.35 131.9 Buy
AAPL 144.66 0.99 2414.03 5.18 25755981 145.09 89.14 Buy
  Ticker                         Name   Price    Change           Cap    EPS   Earnings     IV Rating      Volumen  SMA200    RSI  Stoch  52Whigh  52Wlow Weekly
0      A     Agilent Technologies Inc  148.98  0.107505  4.539390e+10   4.14  8/17/2021  22.64    buy    1973342.0  122.73  65.71  92.53   149.19   88.07  False
1     AA                   Alcoa Corp   36.47 -3.823830  6.809861e+09   4.19  7/15/2021  55.85    buy    8577484.0   25.84  52.18  81.14    44.42   10.43   True
2    AAL  American Airlines Group Inc   21.01 -2.188079  1.347546e+10  -8.49  7/22/2021  47.77   hold   23985527.0   18.32  31.37  17.21    26.09   10.63   True
3    AAP     Advance Auto Parts, Inc.  208.70 -1.113480  1.365710e+10  10.91  8/17/2021  26.62    buy     540639.0  172.21  66.23  95.21   211.16  131.90  False
4   AAPL                    Apple Inc  142.02  1.471847  2.369973e+12   5.20  7/27/2021  26.62    buy  108181793.0  125.56  69.22  96.87   145.09   89.14   True
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

DasIch hat geschrieben: Freitag 9. Juli 2021, 12:28 Es ist allerdings kein wesentlich Faktor weil es andere Baustellen gibt der mehrere Größenordnungen langsamer sind.
Selbstverständlich, gar keine Frage. Dennoch möchte ich zu bedenken geben: Multiprocessing und Multithreading sind relativ komplexe Geschichten, und... es erscheint mir einigermaßen schwierig, wenn ein offensichtlicher Anfänger mit solchen Dingen beschäftigt ist und sie sogar schon in einem Projekt benutzt, ohne zumindest die Basics erlernt und verstanden zu haben. Darum erschien und erscheint es mir in diesem Fall zielführender, erst einmal solche offensichtlichen Dinge glattzuziehen, in der (heimlichen) Hoffnung, daß das möglichst frühzeitig zu einem etwas bessen Verständnis führt.

Allerdings sehe ich in den letzten Jahren ohnehin einen Trend, daß viele Menschen gerne ein bestimmtes Problem lösen wollen, nichts fertiges und für ihren Anwendungsfall nutzbares finden und dann realisieren, daß sie wahrscheinlich ein eigenes Programm dafür schreiben müssen -- und sich dann nur sehr oberflächlich oder sogar gar nicht mit den Grundlagen der Programmierung oder der auserkorenen Sprache beschäftigen, sondern sich sofort auf ihr Problem stürzen in der Hoffnung, das mit dem Programmieren und der Sprache quasi "nebenbei" zu lernen. Nicht selten kopieren sie sich dann irgendwas aus dem Netz und basteln per Trial & Error dann so lange daran herum, gerne auch mit Hilfe von Foren wie diesem, bis sie etwas haben, das zwar irgendwie läuft und irgendetwas tut -- für oft recht beliebige Definitionen von "irgendwie", "irgendetwas", "läuft" und "tut" -- aber das sie nicht im Ansatz verstanden haben und ihnen früher oder später auf die Füße fällt. Aus verschiedenen Gründen habe ich tiefe Zweifel, daß das sonderlich zielführend ist... nicht zuletzt auch darum, weil ich derartige Diskussionen in mannigfaltigsten Ausführungen schon von Benutzern kenne, die sich -- mal so ganz nebenbei aus Neugierde -- ein Linux installieren, dann feststellen, daß es anders funktioniert als ihr gewohntes System, und dann manchmal über Stunden, in einigen Fällen auch über Tage, Wochen und sogar Monate hinweg über dieses doofe Linux ranten. Okay, zumindest jüngere Menschen schauen sich manchmal wenigstens einige Videos mit Tutorials auf populären Videoplattformen an -- und manche davon sind dabei sogar richtig schnell auf dem richtigen Gleis.
DasIch hat geschrieben: Freitag 9. Juli 2021, 12:28
Nun könnte man zwar vermuten, daß die zweite Variante eine neue Speicherallokation provoziert[...]
Könnte man wenn man von Python keine Ahnung hat,
Natürlich, deswegen ja auch der Konjunktiv. ;-)
DasIch hat geschrieben: Freitag 9. Juli 2021, 12:28 Das Copy Modul existiert um Objekte zu kopieren, ist größtenteils unnötig und spielt hier keine Rolle da es nicht benutzt wird (und auch nicht werden sollte).
Oh... neinneinein. Ich wollte mit dem kleinen Hinweis vielmehr darauf hinaus, daß Pythons Speicherverwaltung hin und wieder sogar etwas... überenthusiastisch optimiert. Jedenfalls in früheren Versionen -- ohne genauer hingeschaut zu haben, ist mein subjektiver Eindruck jedenfalls, daß die Optimierung immer besser funktioniert und deren gezielte Umgehung immer seltener notwendig wird. ;-)
DasIch hat geschrieben: Freitag 9. Juli 2021, 12:28 CPython hat einen eigenen Memory Manager der für Allokationen genutzt wird. Der baut auf malloc auf und somit ist tief unter der Haube irgendwo ein malloc versteckt wird aber i.d.R. nicht passieren wenn man ein Objekt erstellt.
Ja, klar. Skriptsprache halt, das machen andere ja ähnlich -- wenngleich (mein Eindruck) meistens nicht so stark optimiert wie in Python. Aber trotz aller Optimierungen und nifty tricks läuft natürlich auch Python in der Regel auf einer Hardware, und schon das geht nun einmal mit entsprechenden Limitierungen einher: Speichergrößen und -Bandbreite(n) der jeweiligen Speicher, ... und mit Ausnahme von Micropython läuft Python nun einmal meistens auf einem Betriebssystem mit einer Speicherverwaltung, die wiederum ihre eigenen Charakteristiken und Optimierungen mitbringt.
DasIch hat geschrieben: Freitag 9. Juli 2021, 12:28 Damit unterscheidet sich Python da wesentlich von Sprachen wie C, C++ oder Rust wo man bei einer Heap Allokationen normalerweise ein malloc hat. Das ist auch essentiell für die Performance einer solchen Sprache wo man anders als in C, C++ oder Rust nahezu alles auf der Heap statt auf dem Stack allokiert.
Hmmm... bei Rust auch? Hat Rust denn nicht auch eine eigene Speicherverwaltung mit Garbage Collection?
DasIch hat geschrieben: Freitag 9. Juli 2021, 12:28 Die JVM ist hier ein besonders gutes Beispiel, da die sich gleich beim Startup eine ganze Heap allokiert um zur Laufzeit möglichst nie malloc aufzurufen.
Hmmm, Java... eine Erfahrung, die ich gerne so schnell und nachhaltig wie möglich vergessen möchte. Daher vielen Dank für Deine Antwort, aber immer wenn Java erwähnt wird, muß ich dringend zu Youtube und das hier [1] anschauen... mittlerweile zum Glück nur noch höchstens zwei- oder dreimal, immerhin. ;-)

[1] https://www.youtube.com/watch?v=PUCgC_TukKg
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

LukeNukem hat geschrieben: Freitag 9. Juli 2021, 15:49Ich wollte mit dem kleinen Hinweis vielmehr darauf hinaus, daß Pythons Speicherverwaltung hin und wieder sogar etwas... überenthusiastisch optimiert. Jedenfalls in früheren Versionen -- ohne genauer hingeschaut zu haben, ist mein subjektiver Eindruck jedenfalls, daß die Optimierung immer besser funktioniert und deren gezielte Umgehung immer seltener notwendig wird.
Von welchen Optimierungen faselst Du denn da? Und wie oder was umgehst Du? Ich habe immer noch das Gefühl, dass Du noch nicht verstanden hast, was Referenzen sind.
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

mirko3107 hat geschrieben: Freitag 9. Juli 2021, 13:51 Könnt ihr euren Streit nicht per PN klären?
Bitte entschuldige, Du hast natürlich Recht.
mirko3107 hat geschrieben: Freitag 9. Juli 2021, 13:51 @Topic: Mein Dataframe wird immer noch nicht beschrieben, hab alle möglichen Optionen probiert.
Verzeihung, welcher Dataframe wird denn nicht beschrieben? Die einzelnen Dateframes aus Deinen Funktionen oder am Ende der "Gesamt-Dataframe", der aus jenen in Deinen Funktionen konkateniert werden soll? Was hast Du bereits ausprobiert, und inwiefern hat es nicht funktioniert? Gab es Fehlermeldungen, und wenn ja, welche? Und wie sieht der Code aus, mit dem Du es ausprobiert hast?
mirko3107
User
Beiträge: 75
Registriert: Freitag 23. April 2021, 15:42

Ich lese aus einer csv ein Dataframe, schreibe Werte in das Dataframe ( hatte ich jedenfalls gedacht) und am Ende sind alle Werte unverändert.

Die Funktion sieht so aus:

Code: Alles auswählen

def yahoo():
    watch = pd.read_csv("watch.csv", usecols=['Ticker', 'Name', 'Price', 'Change', 'Cap', 'EPS', 'Volumen', '52Whigh', '52Wlow', 'Rating'])
    print(watch.head(5))
    rows = len(watch.index)
    while True:
        for i in range(0, 5):
            try:
                ticker = watch['Ticker'].values[i]
                quote_url = 'https://query1.finance.yahoo.com/v7/finance/quote?symbols='+ticker
                quote_req = requests.get(quote_url)
                change = round(quote_req.json()['quoteResponse']['result'][0].get('regularMarketChangePercent', 0),2)
                price = round(quote_req.json()['quoteResponse']['result'][0].get('regularMarketPrice', 0),2)
                volume = quote_req.json()['quoteResponse']['result'][0].get('regularMarketVolume', 0)
                W52high = round(quote_req.json()['quoteResponse']['result'][0].get('fiftyTwoWeekHigh', 0),2)
                W52low = round(quote_req.json()['quoteResponse']['result'][0].get('fiftyTwoWeekLow', 0),2)
                rat1 = quote_req.json()['quoteResponse']['result'][0].get('averageAnalystRating', 'None')
                rating=''
                for i in rat1:
                    if i.isalpha():
                        rating = "".join([rating, i])
                if 'epsCurrentYear' in quote_req.json()['quoteResponse']['result'][0]:
                    eps = round(quote_req.json()['quoteResponse']['result'][0].get('epsCurrentYear', 0),2)
                else:
                    eps = '0'
                cap = round(quote_req.json()['quoteResponse']['result'][0].get('marketCap', 0)/1000000000, 2)
                print(ticker,price,change,cap,eps,volume,W52high,W52low,rating)
                time.sleep(0.5)
                watch.at[i, 'Price'] = price
                watch.at[i, 'Change'] = change
                watch.at[i, 'Cap'] = cap
                watch.at[i, 'EPS'] = eps
                watch.at[i, 'Volumen'] = volume
                watch.at[i, '52Whigh'] = W52high
                watch.at[i, '52Wlow'] = W52low
                watch.at[i, 'Rating'] = rating
            except (KeyError, IndexError, RemoteDataError) as error:
                print(f"Failed reading {ticker}: {error}")
                pass
        print(watch.head(5))
        watch.to_csv('watch2.csv', header=True, index=False, columns=['Ticker', 'Name', 'Price', 'Change', 'Cap', 'EPS', 'Volumen', '52Whigh', '52Wlow', 'Rating'])
        time.sleep(600)
Oben das Dataframe aus der CSV, dann die Werte, die reinsollen und unten das Dataframe, wie es wieder in die CSV geschrieben wird.

Code: Alles auswählen

  Ticker                         Name   Price    Change           Cap    EPS Rating    Volumen  52Whigh  52Wlow
0      A     Agilent Technologies Inc  148.98  0.107505  4.539390e+10   4.14    buy    1973342   149.19   88.07
1     AA                   Alcoa Corp   36.47 -3.823830  6.809861e+09   4.19    buy    8577484    44.42   10.43
2    AAL  American Airlines Group Inc   21.01 -2.188079  1.347546e+10  -8.49   hold   23985527    26.09   10.63
3    AAP     Advance Auto Parts, Inc.  208.70 -1.113480  1.365710e+10  10.91    buy     540639   211.16  131.90
4   AAPL                    Apple Inc  142.02  1.471847  2.369973e+12   5.20    buy  108181793   145.09   89.14
A 149.37 0.36 45.33 4.14 195631 149.71 88.07 Buy
AA 37.6 6.33 7.02 4.27 2698158 44.42 10.43 Buy
AAL 20.91 2.73 13.41 -8.54 9537317 26.09 10.63 Hold
AAP 211.65 2.0 13.85 10.92 118807 212.35 131.9 Buy
AAPL 145.06 1.27 2420.7 5.18 38741349 145.44 89.14 Buy
  Ticker                         Name   Price    Change           Cap    EPS Rating      Volumen  52Whigh  52Wlow
0      A     Agilent Technologies Inc  148.98  0.107505  4.539390e+10   4.14    buy    1973342.0   149.19   88.07
1     AA                   Alcoa Corp   36.47 -3.823830  6.809861e+09   4.19    buy    8577484.0    44.42   10.43
2    AAL  American Airlines Group Inc   21.01 -2.188079  1.347546e+10  -8.49   hold   23985527.0    26.09   10.63
3    AAP     Advance Auto Parts, Inc.  208.70 -1.113480  1.365710e+10  10.91    buy     540639.0   211.16  131.90
4   AAPL                    Apple Inc  142.02  1.471847  2.369973e+12   5.20    buy  108181793.0   145.09   89.14
mirko3107
User
Beiträge: 75
Registriert: Freitag 23. April 2021, 15:42

Fehlermeldung kommt keine.
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

mirko3107 hat geschrieben: Freitag 9. Juli 2021, 16:14 Ich lese aus einer csv ein Dataframe, schreibe Werte in das Dataframe ( hatte ich jedenfalls gedacht) und am Ende sind alle Werte unverändert.

Code: Alles auswählen

  Ticker                         Name   Price    Change           Cap    EPS Rating    Volumen  52Whigh  52Wlow
0      A     Agilent Technologies Inc  148.98  0.107505  4.539390e+10   4.14    buy    1973342   149.19   88.07
1     AA                   Alcoa Corp   36.47 -3.823830  6.809861e+09   4.19    buy    8577484    44.42   10.43
2    AAL  American Airlines Group Inc   21.01 -2.188079  1.347546e+10  -8.49   hold   23985527    26.09   10.63
3    AAP     Advance Auto Parts, Inc.  208.70 -1.113480  1.365710e+10  10.91    buy     540639   211.16  131.90
4   AAPL                    Apple Inc  142.02  1.471847  2.369973e+12   5.20    buy  108181793   145.09   89.14
Könntest Du diesen -- nur diesen -- Teil bitte einmal in eine CSV-Datei schreiben und sie hier als Code posten?
mirko3107
User
Beiträge: 75
Registriert: Freitag 23. April 2021, 15:42

Die gespeicherte CSV sieht genauso aus wie die geladene.

Code: Alles auswählen

Ticker,Name,Price,Change,Cap,EPS,Volumen,52Whigh,52Wlow,Rating
A,Agilent Technologies Inc,148.98,0.107505,45393903616.0,4.14,1973342.0,149.19,88.07,buy
AA,Alcoa Corp,36.47,-3.82383,6809860608.0,4.19,8577484.0,44.42,10.43,buy
AAL,American Airlines Group Inc,21.01,-2.1880789,13475456000.0,-8.49,23985527.0,26.09,10.63,hold
AAP,"Advance Auto Parts, Inc.",208.7,-1.11348,13657098240.0,10.91,540639.0,211.16,131.9,buy
AAPL,Apple Inc,142.02,1.4718473,2369972862976.0,5.2,108181793.0,145.09,89.14,buy
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

mirko3107 hat geschrieben: Freitag 9. Juli 2021, 18:39 Die gespeicherte CSV sieht genauso aus wie die geladene.
Okay, dankeschön. Bitte gib' mir ein paar Minuten, ja? ;-)
mirko3107
User
Beiträge: 75
Registriert: Freitag 23. April 2021, 15:42

werd mich die Tage mal ransetzen und mich mit diesem Vorschlag befassen:

Code: Alles auswählen

QUOTE_URL = "https://query1.finance.yahoo.com/v7/finance/quote"

def query_quote(tickers):
    quote = []
    for ticker in tickers:
        try:
            quote_response = requests.get(QUOTE_URL, params={"symbols": ticker})
            result = quote_response.json()['quoteResponse']['result'][0]
        except (KeyError, IndexError, RemoteDataError) as error:
            print(f"Failed reading {ticker}: {error}")
            quote.append((ticker, 0, 0, 0, 0))
        else:
            quote.append((
                ticker,
                round(result.get('regularMarketPrice', 0),2),
                round(result.get('regularMarketChangePercent', 0),2),
                round(result.get('marketCap', 0)/1000000000, 2),
                round(result.get('epsCurrentYear', 0),2)
            ))
        time.sleep(1)
    return pd.DataFrame(quote, columns=['Ticker', 'Price', 'Change', 'Cap', 'EPS'])
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

mirko3107 hat geschrieben: Freitag 9. Juli 2021, 19:38 werd mich die Tage mal ransetzen und mich mit diesem Vorschlag befassen:
Vielleicht gefällt Dir ja auch das hier:

Code: Alles auswählen

#!/usr/bin/env python
import sys
import time
import logging
import multiprocessing
import concurrent.futures

import requests
import pandas as pd


logging.basicConfig(
    stream=sys.stdout,
    level=logging.INFO,
    format='%(asctime)s ° %(filename)s(%(lineno)d) ° %(levelname)s ° %(message)s'
)


class YahooFun:
    QUOTE_URL = 'https://query1.finance.yahoo.com/v7/finance/quote?symbols={}'
    FILENAME = 'yahoo.csv'

    def __init__(self, filename=None):
        self._filename = filename or self.FILENAME
        self._df = pd.read_csv(self.filename, sep=';')
        self.manager = multiprocessing.Manager()
        self.lock = self.manager.Lock()

    def yahoo(self, idx, ticker):
        try:
            pc_start = time.perf_counter()
            url = self.QUOTE_URL.format(ticker)
            quote_req = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'}).json()['quoteResponse']['result'][0]
            with self.lock:
                self._df.loc[idx, 'Change'] = quote_req.get('regularMarketChangePercent', 0)
                self._df.loc[idx, 'Price'] = quote_req.get('regularMarketPrice', 0)
                self._df.loc[idx, 'EPS'] = quote_req.get('epsCurrentYear', 0)
                self._df.loc[idx, 'Cap'] = quote_req.get('marketCap', 0) / 1000000000
            duration = time.perf_counter() - pc_start
            logging.info('"{}" took {:0.4f} ms'.format(url, duration*1000))
            return duration
        except Exception as e: # not so nice, but for demo purposes...
            logging.exception('Something went terribly wrong')
            return 0.0

    @property
    def df(self):
        return self._df

    @property
    def filename(self):
        return self._filename
    

if __name__ == '__main__':
    yf = YahooFun()
    print(yf.df)
    with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
        futures = [executor.submit(yf.yahoo, idx, yf.df.iloc[idx].Ticker) for idx in yf.df.index]
    logging.info('insgesamt: {} ms'.format(sum([future.result() * 1000 for future in futures])))
    print(yf.df.round(2))
Ich hab' mal die Kommentare weggelassen und das Ganze ein bisschen eingedampft... Wenn Du Fragen hast, bitte tu' Dir keinen Zwang an. ;-)
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@LukeNukem: wenn man keinen Setter hat, sind die Properties eigentlich überflüssig.
Du hast Dir sicher etwas dabei gedacht, einen Multiprocessing-Lock mit einem ThreadPoolExecutor zu mischen.
submit hat den Vorteil, dass man da einen Rückgabewert benutzen kann, den Du aber nicht wirklich nutzt. Und das Ergebnis eines Requests ist immer noch kein req(uest).
Statt executor.submit und future.result würde man einfach execute.map benutzen.

Code: Alles auswählen

class YahooFun:
    QUOTE_URL = 'https://query1.finance.yahoo.com/v7/finance/quote?symbols={}'
    FILENAME = 'yahoo.csv'

    def __init__(self, filename=None):
        self.filename = filename or self.FILENAME
        self.df = pd.read_csv(self.filename, sep=';')

    def yahoo(self, ticker):
        try:
            url = self.QUOTE_URL.format(ticker)
            response = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'}).json()
            result = response['quoteResponse']['result'][0]
            return [
                quote_req.get('regularMarketChangePercent', 0),
                quote_req.get('regularMarketPrice', 0),
                quote_req.get('epsCurrentYear', 0),
                quote_req.get('marketCap', 0) / 1000000000
            ]
        except Exception as e: # not so nice, but for demo purposes...
            logging.exception('Something went terribly wrong')
            return [0.0] * 4


def main():
    yf = YahooFun()
    print(yf.df)
    with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
        result = list(executor.map(yf.yahoo, yf.df.Ticker))
    df = pd.DataFrame(result, columns=["Change", "Price", "EPS", "Cap"])
    print(df.round(2))

if __name__ == '__main__':
    main()
Aber jetzt braucht ja die yahoo-Methode gar kein self mehr. Wir haben also gar keine Klasse:

Code: Alles auswählen

QUOTE_URL = 'https://query1.finance.yahoo.com/v7/finance/quote?symbols={}'
FILENAME = 'yahoo.csv'

def query_yahoo(ticker):
    try:
        response = requests.get(QUOTE_URL, params={"symbols":ticker}, headers={'User-Agent': 'Mozilla/5.0'}).json()
        result = response['quoteResponse']['result'][0]
        return [
            quote_req.get('regularMarketChangePercent', 0),
            quote_req.get('regularMarketPrice', 0),
            quote_req.get('epsCurrentYear', 0),
            quote_req.get('marketCap', 0) / 1000000000
        ]
    except Exception as e: # not so nice, but for demo purposes...
        logging.exception('Something went terribly wrong')
        return [0.0] * 4


def main():
    yf_df = pd.read_csv(self.filename, sep=';')
    print(yf_df)
    with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
        result = list(executor.map(query_yahoo, yf_df.Ticker))
    df = pd.DataFrame(result, columns=["Change", "Price", "EPS", "Cap"])
    print(df.round(2))

if __name__ == '__main__':
    main()
Am besten fängt man andersherum an, man schreibt Funktionen, und sobald man merkt, das man mehrere Funktionen hat, die den selben Zustand brauchen, packt man die in eine Klasse.
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

Sirius3 hat geschrieben: Freitag 9. Juli 2021, 20:40 @LukeNukem:
Ach, schade, letztens hattest Du mir noch versprochen, daß Du mir nicht mehr schreibst, und ich hatte mich schon gefreut. Aber ich hab' gute Laune und möchte für mich -- zumal ich mich ja dämlicherweise an den Störungen des Threads beteiligt habe -- jetzt erstmal dem TO helfen. Können wir unseren Zwist daher einstweilen zurückstellen?

Wenn wir dem TO geholfen haben, kann ich Dir gerne erklären, welche Gründe ich für jede einzelne der von Dir kritisierten Designentscheidungen hatte und habe. Aber jetzt ist erstmal der TO dran, ja?
mirko3107
User
Beiträge: 75
Registriert: Freitag 23. April 2021, 15:42

LukeNukem hat geschrieben: Freitag 9. Juli 2021, 20:09
mirko3107 hat geschrieben: Freitag 9. Juli 2021, 19:38 werd mich die Tage mal ransetzen und mich mit diesem Vorschlag befassen:
Vielleicht gefällt Dir ja auch das hier:

Ich hab' mal die Kommentare weggelassen und das Ganze ein bisschen eingedampft... Wenn Du Fragen hast, bitte tu' Dir keinen Zwang an. ;-)
Hab bei "self._df = pd.read_csv(self.filename, sep=';')" das Semikolon durch ein Komma ersetzt, jetzt rennt dein Script.
Danke dir für deine Mühe, läuft super und darauf kann ich aufbauen.
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@LukeNukem: Sirius3 hat mit der Verbesserung des Vorschlags für den TO ja dem TO geholfen. Aber Du siehst das ja gleich wieder als Zwist und persönlichen Angriff auf Dich. Und drückst Dich um eine Antwort die ja auch dem TO helfen würde den Code den er da zu seinem Problem bekommen hat, besser einordnen zu können…
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

Sirius3 hat geschrieben: Freitag 9. Juli 2021, 20:40 wenn man keinen Setter hat, sind die Properties eigentlich überflüssig.
Die Properties sorgen dafür, daß die internen Variablen für alle readonly sind, die die Konvention kennen.
Sirius3 hat geschrieben: Freitag 9. Juli 2021, 20:40 Du hast Dir sicher etwas dabei gedacht, einen Multiprocessing-Lock mit einem ThreadPoolExecutor zu mischen.
Ja, MP-Locks funktionieren auch auf Threads. Prozeßlocks sind zwar ein wenig langsamer, dafür aber auch mit Prozessen nutzbar. Ich weiß ja, daß der TO noch ganz andere Dinge vorhat.
Sirius3 hat geschrieben: Freitag 9. Juli 2021, 20:40 submit hat den Vorteil, dass man da einen Rückgabewert benutzen kann, den Du aber nicht wirklich nutzt.
Sicher. Aber ich weiß, daß der TO noch ganz andere Dinge vorhat.
Sirius3 hat geschrieben: Freitag 9. Juli 2021, 20:40 Und das Ergebnis eines Requests ist immer noch kein req(uest).
Nein, das stimmt. Aber wenn Du jemanden etwas zeigen möchtest, funktioniert das mir einem bekannten Aufhänger nun einmal besser.
Sirius3 hat geschrieben: Freitag 9. Juli 2021, 20:40 Statt executor.submit und future.result würde man einfach execute.map benutzen.
Das würde für mein Beispiel passen, klar... jedenfalls, wenn ich nicht wüßte, daß der TO noch sehr viel mehr vorhat.
Sirius3 hat geschrieben: Freitag 9. Juli 2021, 20:40 Am besten fängt man andersherum an, man schreibt Funktionen, und sobald man merkt, das man mehrere Funktionen hat, die den selben Zustand brauchen, packt man die in eine Klasse.
Ich glaube nicht an Standardrezepte. Aussagen wie "man tut dies" oder "man macht nicht jenes" sind mir ausgesprochen suspekt, und ich bin auch nicht irgendein "man".

In meiner Welt verfolgt Softwareentwicklung mehr oder weniger bestimmte -- nicht selten sogar veränderliche -- Ziele.

Meine Implementierung folgt daher einigen Grundgedanken. Erstens möchte ich dem TO gerne verständlich zeigen, wie so etwas elegant, performant, und wartbar geht. Zweitens möchte ich ihm die Möglichkeit geben, auf meinem Code aufzubauen. Drittens weiß ich, daß der TO noch sehr viel mehr vorhat, und möchte ihm passende Schnittstellen und Erweiterungsmöglichkeiten aufzeigen. Viertens halte ich Erweiterbarkeit für eine wichtige Eigenschaft jedes guten Designs -- und wenn jetzt jemand "Overengineering" sagt, disqualifiziert er sich selbst. ;-)
mirko3107
User
Beiträge: 75
Registriert: Freitag 23. April 2021, 15:42

Find deine Hilfe absolut klasse, vielen Dank.

Hab den Part mit self.lock noch um ein paar Werte erweitert.

Code: Alles auswählen

with self.lock:
	self._df.loc[idx, 'Change'] = quote_req.get('regularMarketChangePercent', 0)
	self._df.loc[idx, 'Price'] = quote_req.get('regularMarketPrice', 0)
	self._df.loc[idx, 'EPS'] = quote_req.get('epsCurrentYear', 0)
	self._df.loc[idx, 'Cap'] = quote_req.get('marketCap', 0) / 1000000000
	self._df.loc[idx, 'Volumen'] = quote_req.get('regularMarketVolume', 0)
	self._df.loc[idx, '52Whigh']= quote_req.get('fiftyTwoWeekHigh', 0)
	self._df.loc[idx, '52Wlow'] = quote_req.get('fiftyTwoWeekLow', 0)
	self._df.loc[idx, 'Rating'] = quote_req.get('averageAnalystRating', 'None')
Wie bekomm ich mein gebasteltes hier eingabaut?

Code: Alles auswählen

rat1 = quote_req.json()['quoteResponse']['result'][0].get('averageAnalystRating', 'None')
rating=''
	for i in rat1:
		if i.isalpha():
rating = "".join([rating, i])
Oder gibts da noch bessere Möglichkeit als nur Buchstaben rausfiltern?

Danke
mirko3107
User
Beiträge: 75
Registriert: Freitag 23. April 2021, 15:42

Hab eine andere Quelle für das Rating gefunden:

Code: Alles auswählen

#!/usr/bin/env python
import sys
import time
import logging
import multiprocessing
import concurrent.futures

import requests
import pandas as pd


logging.basicConfig(
    stream=sys.stdout,
    level=logging.INFO,
    format='%(asctime)s ° %(filename)s(%(lineno)d) ° %(levelname)s ° %(message)s'
)


class YahooFun:
    QUOTE_URL = 'https://query1.finance.yahoo.com/v7/finance/quote?symbols={}'
    QUOTE2_URL = 'https://query2.finance.yahoo.com/v10/finance/quoteSummary/{}?formatted=true&crumb=swg7qs5y9UP&lang=en-US&region=US&' \
                 'modules=upgradeDowngradeHistory,recommendationTrend,' \
                 'financialData,earningsHistory,earningsTrend,industryTrend&' \
                 'corsDomain=finance.yahoo.com'
    FILENAME = 'yahoo2.csv'

    def __init__(self, filename=None):
        self._filename = filename or self.FILENAME
        self._df = pd.read_csv(self.filename, sep=',')
        self.manager = multiprocessing.Manager()
        self.lock = self.manager.Lock()

    def yahoo(self, idx, ticker):
        try:
            pc_start = time.perf_counter()
            url = self.QUOTE_URL.format(ticker)
            rating_url = self.QUOTE2_URL.format(ticker)
            quote_req = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'}).json()['quoteResponse']['result'][0]
            quote_rating = requests.get(rating_url, headers={'User-Agent': 'Mozilla/5.0'}).json()['quoteSummary']['result'][0]['financialData']
            with self.lock:
                self._df.loc[idx, 'Change'] = quote_req.get('regularMarketChangePercent', 0)
                self._df.loc[idx, 'Price'] = quote_req.get('regularMarketPrice', 0)
                self._df.loc[idx, 'EPS'] = quote_req.get('epsCurrentYear', 0)
                self._df.loc[idx, 'Cap'] = quote_req.get('marketCap', 0) / 1000000000
                self._df.loc[idx, 'Volumen'] = quote_req.get('regularMarketVolume', 0)
                self._df.loc[idx, '52Whigh']= quote_req.get('fiftyTwoWeekHigh', 0)
                self._df.loc[idx, '52Wlow'] = quote_req.get('fiftyTwoWeekLow', 0)
                self._df.loc[idx, 'Rating'] = quote_rating.get('recommendationKey', 'None')
            duration = time.perf_counter() - pc_start
            logging.info('"{}" took {:0.4f} ms'.format(url, duration*1000))
            return duration
        except Exception as e: # not so nice, but for demo purposes...
            logging.exception('Something went terribly wrong')
            return 0.0

    @property
    def df(self):
        return self._df

    @property
    def filename(self):
        return self._filename


if __name__ == '__main__':
    yf = YahooFun()
    print(yf.df)
    with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
        futures = [executor.submit(yf.yahoo, idx, yf.df.iloc[idx].Ticker) for idx in yf.df.index]
    logging.info('insgesamt: {} ms'.format(sum([future.result() * 1000 for future in futures])))
    csv = (yf.df.round(2))
    csv.to_csv('yahoo2.csv',  header=True, index=False)
Antworten