Topic: Individuazione di valori numerici maggiori da una lista  (Letto 181 volte)

0 Utenti e 1 Visitatore stanno visualizzando questo topic.

Offline PyAnam

  • python unicellularis
  • *
  • Post: 7
  • Punti reputazione: 0
    • Mostra profilo
Individuazione di valori numerici maggiori da una lista
« il: Novembre 11, 2021, 18:30 »
Salve a tutti, ho un problema che non riesco a risolvere. I dati di partenza sono composti da due serie di valori (fee and time) contenuti in un file test.csv . Da questi valori devo trovare il valore fee maggiore di tutti e poi un secondo valore fee minore del primo valore trovato (il maggiore di tutti) ma maggiore di tutti gli altri, il quale però deve essere successivo sulla scala time al primo valore maggiore di tutti e deve avere prima di se un valore fee più basso di lui. Per capirsi il risultato dovrebbe essere per il primo valore maggiore di tutti il valore fee 1200 (time 50), mentre per il secondo valore maggiore il valore fee 860 (time 80).

Il file test.csv è così composto:

fee,time
500,100
700,90
860,80
800,70
1000,60
1200,50
1100,40
1050,30

Non riesco a risolvere  :confused: qualcuno ha un'idea?
grazie infinite a tutti

Offline nuzzopippo

  • python sapiens
  • *****
  • Post: 501
  • Punti reputazione: 0
    • Mostra profilo
Re:Individuazione di valori numerici maggiori da una lista
« Risposta #1 il: Novembre 12, 2021, 09:39 »
Mi è un po' difficile rispondere ad una domanda del genere, non conoscendo a quale livello ed a quale fine venga posta una problematica così sui genereris (chi se le inventa, e perché?) ... comunque, supponiamo un file "dati.csv" contenente i dati da Te indicati, lo apriamo, leggiamo i valori scritti assegnandoli ad una lista di dizionari
>>> import csv
>>> with open('dati.csv', 'r') as f:
data = list(csv.DictReader(f, delimiter=','))


>>> for r in data:
print(r)


{'fee': '500', 'time': '100'}
{'fee': '700', 'time': '90'}
{'fee': '860', 'time': '80'}
{'fee': '800', 'time': '70'}
{'fee': '1000', 'time': '60'}
{'fee': '1200', 'time': '50'}
{'fee': '1100', 'time': '40'}
{'fee': '1050', 'time': '30'}

ordiniamo inversamente la lista sulla base dei valori interi di "fee", il primo elemento sarà il primo da trovate
>>> data.sort(reverse=True, key=lambda x: int(x['fee']))
>>> for r in data:
print(r)


{'fee': '1200', 'time': '50'}
{'fee': '1100', 'time': '40'}
{'fee': '1050', 'time': '30'}
{'fee': '1000', 'time': '60'}
{'fee': '860', 'time': '80'}
{'fee': '800', 'time': '70'}
{'fee': '700', 'time': '90'}
{'fee': '500', 'time': '100'}
>>> p = data[0]

estraiamo gli elementi con "fee" maggiore di p, avremo una lista già ordinata per "fee"
>>> sub_data = [x for x in data if int(x['fee']) < int(p['fee'])and int(x['time']) > int(p['time'])]
>>> for r in sub_data:
print(r)


{'fee': '1000', 'time': '60'}
{'fee': '860', 'time': '80'}
{'fee': '800', 'time': '70'}
{'fee': '700', 'time': '90'}
{'fee': '500', 'time': '100'}

se ora estraiamo la lista degli elementi con "time" maggiore del primo (coincidenza, lo sono tutti, prova mischiando meglio time) escludendo il primo, il primo della nuova lista avrà dei valori "fee" minori di lui e sarà di valore "time" maggiore del primo elemento escluso, il che mi sembra sia l'ingarbugliata condizione posta
>>> s = [x for x in sub_data[1:] if int(x['time']) > int(sub_data[0]['time'])][0]
>>> p
{'fee': '1200', 'time': '50'}
>>> s
{'fee': '860', 'time': '80'}
>>>


... ammesso che io abbia capito bene, prova a vedere se è questo che cerchi

[Edit] rileggendo, mi accorgo ora che la lista originaria è ordinata in ragione inversa del tempo ... non è che bisogna rispettare la progressione temporale vero?, perché in tal caso la logica sarebbe del tutto diversa : bisognerebbe individuare l'indice del  massimo "fee" e poi ciclare in ordine inverso gli elementi della sotto-lista determinata da tale indice sin quando non si riscontra un valore crescente di "fee".
Sbrigativamente :
>>> import csv
>>> with open('dati.csv', 'r') as f:
data = list(csv.DictReader(f, delimiter=','))

>>> m_i = [int(x['fee']) for x in data].index(max([int(x['fee']) for x in data]))
>>> p = data[m_i]
>>> p
{'fee': '1200', 'time': '50'}
>>> sub_data = data[:m_i]
>>> for i in range(len(sub_data)-1, -1, -1):
elem = sub_data[i]
if elem['fee'] > sub_data[i+1]['fee']:
break


>>> s = elem
>>> s
{'fee': '860', 'time': '80'}
>>>

[Ri-Edit] ... e riflettendo, mi son reso conto che in caso vi siano dei valori massimi uguali si è nei guai (oltre ad accorgermi di un errore logico commesso e ora corretto), forse è meglio utilizzare una funzione pseudo-ricorsiva tipo questa
>>> def get_elements(c, data):
elements = []
if not c:
maximo = max([int(x['fee']) for x in data])
for i in range(len(data) -1, -1, -1):
if int(data[i]['fee']) == maximo:
elements.append(data[i])
break
elements.append(get_elements(True, data[:i]))
else:
for i in range(len(data) -2, -1, -1):
if int(data[i]['fee']) > int(data[i + 1]['fee']):
return data[i]
return elements

>>> p, s = get_elements(False, data)
>>> p
{'fee': '1200', 'time': '50'}
>>> s
{'fee': '860', 'time': '80'}
>>>

che estrae il primo elemento massimo lungo la scala temporale e poi procede nel tempo sino a trovare il primo elemento crescente.

Penso che sia questa ultima la soluzione che in effetti cerchi ... purtroppo, mi capita spesso di dover guardare più volte prima di capire cosa si intende, comunque dai conferma se alla fine ho centrato il "problema"
« Ultima modifica: Novembre 12, 2021, 10:41 da nuzzopippo »

Offline PyAnam

  • python unicellularis
  • *
  • Post: 7
  • Punti reputazione: 0
    • Mostra profilo
Re:Individuazione di valori numerici maggiori da una lista
« Risposta #2 il: Novembre 12, 2021, 15:04 »
Grazie mille nuzzopippo e scusa per l'ingarbugliatezza del tutto... il tutto servirebbe per poter individuare due punti in un grafico attraverso i quali far passare una retta (immagine a questo link https://drive.google.com/file/d/1nb7Vk4BJYpRih8jOweZrN-Ij5_zZ7zUV/view?usp=sharing ).
La tua soluzione è eccezionale e funziona quasi alla perfezione, questo credo a causa di un mio errore di esposizione per il quale chiedo umilmente scusa. Per correggermi e spiegare meglio credo che sia giusto anche aggiungere altri valori alla lista che lascio qui sotto. Nello specifico il secondo valore da trovare deve essere preceduto da un valore minore e seguito da uno minore (quindi una sorta di punta  o apice) ma deve anche essere il secondo come grandezza tra tutti i valori preceduti e seguiti da uno minore. Esempio pratico nel grafico allegato ottenuto con i nuovi dati i punti trovati attraverso i quali far passare la retta dovrebbero essere A e B, mentre con il codice attuale i punti trovati sono A e C.
Forse mi sono ingarbugliato ulteriormente, ma spero che tu riesca a decifrare  :ok:

dati.csv

fee,time
500,100
724,95
127,93
700,90
477,87
999,83
860,80
900,75
800,70
1000,60
1200,50
1100,40
1050,30

Offline nuzzopippo

  • python sapiens
  • *****
  • Post: 501
  • Punti reputazione: 0
    • Mostra profilo
Re:Individuazione di valori numerici maggiori da una lista
« Risposta #3 il: Novembre 12, 2021, 19:03 »
Oddio ... guardato l'immagine non è che si capisca cosa "vorresti", se è semplicemente il punto di massima successivo, potresti quantificarlo trami te la formula già utilizzata per il vero massimo applicata al resto della lista e poi applicarci le verifiche che ritieni necessarie ... ma se non è perfettamente chiaro l'obiettivo da raggiungere ci si gira intorno e basta e da quel grafico, sinceramente, non riesco a comprenderlo sarà perché i miei trascorsi scolastici sono, diciamo, remoti nel tempo.

Personalmente ho più l'impressione che a Te servisse qualcosa come una "retta dei valori medi", comunque chiarisci bene il concetto dell'obiettivo da raggiungere, magari, se stai cercando di applicare un metodo di rappresentazione indicalo, in programmazione di "dovrebbero" avere le idee chiare prima di passare ad implementare, vedi il pasticcio di ipotesi da me creato nel precedente post.

Offline PyAnam

  • python unicellularis
  • *
  • Post: 7
  • Punti reputazione: 0
    • Mostra profilo
Re:Individuazione di valori numerici maggiori da una lista
« Risposta #4 il: Novembre 12, 2021, 20:03 »
in effetti il trovare i due punti attraverso i quali far passare una retta è la prima parte di un problema più complesso in quanto python dovrebbe trovare il punto maggiore di tutti ed un secondo punto che sia il maggiore possibile alla condizione che la retta creata lungo il suo sviluppo non sia inferiore a nessun punto. Nella figura a questo link https://drive.google.com/file/d/1GF3OHkA3-TRu_7CFDiMaCadmOItMhgzv/view?usp=sharing si vede come la retta parta dal primo punto A che è il più alto di tutti, ma il secondo da cui passa B non è di fatto il secondo più alto (se passasse per il secondo più alto che è C poi finirebbe sotto a tutti gli altri). Di fatto python dovrebbe trovare il punto giusto per far passare la retta in modo che tutti gli altri punti rimangano sotto la retta tracciata. Questo può variare a seconda del grafico osservato di volta in volta.

Offline nuzzopippo

  • python sapiens
  • *****
  • Post: 501
  • Punti reputazione: 0
    • Mostra profilo
Re:Individuazione di valori numerici maggiori da una lista
« Risposta #5 il: Novembre 13, 2021, 09:25 »
Guardando la figura credo, finalmente, di aver compreso il Tuo problema, che sarebbe : trovare la retta passante per il punto che presenta la minima pendenza rispetto al punto più alto

... Mi hai fatto venire un vago ricordo di tempi remotissimi.
Posto sia il valore "time" rappresentante l'asse "x" e "fee" le ascisse di una rappresentazione cartesiana, se osservi la figura che Tu stesso hai lincato vedrai che su tale retta giace l'ipotenusa del triangolo rettangolo formato dal Δx  e dal Δy del punto rispetto al punto più alto.
Pertanto dovremmo applicare i teoremi dei triangoli rettangoli e trovare il triangolo che presenta l'angolo minore rispetto al punto più alto che recita, se ricordo bene : "un cateto è uguale all'altro cateto per il seno dell'angolo opposto o per il coseno dell'angolo adiacente"
Ciò che serve a noi è trovare il minimo valore per il seno dell'angolo opposto a  Δy, ossia alla differenza delle ascisse (fee), quindi, limitando la progressione ai soli valori crescenti di ordinata dovremo, quindi, trovare l'angolo più piccolo il cui seno è pari a "Δy/Δx " ... un discorso piuttosto semplice, applichiamolo sugli ultimi dati da Te postati, non lo ho testato facendo il grafico ma così dovrebbe funzionare :
Python 3.8.10 (default, Sep 28 2021, 16:10:42) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license()" for more information.
>>> import csv
>>> with open('pyanam.csv', 'r') as f:
data = list(csv.DictReader(f, delimiter=','))


>>> for r in data:
print(r)


{'fee': '500', 'time': '100'}
{'fee': '724', 'time': '95'}
{'fee': '127', 'time': '93'}
{'fee': '700', 'time': '90'}
{'fee': '477', 'time': '87'}
{'fee': '999', 'time': '83'}
{'fee': '860', 'time': '80'}
{'fee': '900', 'time': '75'}
{'fee': '800', 'time': '70'}
{'fee': '1000', 'time': '60'}
{'fee': '1200', 'time': '50'}
{'fee': '1100', 'time': '40'}
{'fee': '1050', 'time': '30'}
>>> def get_p_min_vertex(d):
data = d[::-1]
elements = []
m_value = str(max([int(x['fee']) for x in data]))
for i in range(len(data)):
if data[i]['fee'] == m_value:
p = data[i]
elements.append(p)
break
sd = data[i+1:]
rapp = [(int(p['fee'])-int(x['fee']))/(int(x['time'])-int(p['time'])) for x in sd]
i_r = rapp.index(min(rapp))
s = sd[i_r]
elements.append(s)
return elements

>>> p, s = get_p_min_vertex(data)
>>> p
{'fee': '1200', 'time': '50'}
>>> s
{'fee': '999', 'time': '83'}
>>>

... ovviamente, se Tu volessi anche la retta inversa (time precedente il punto più alto) la logica sarebbe analoga, con adattamento del sub-set di valori da esaminare
« Ultima modifica: Novembre 13, 2021, 09:32 da nuzzopippo »

Offline PyAnam

  • python unicellularis
  • *
  • Post: 7
  • Punti reputazione: 0
    • Mostra profilo
Re:Individuazione di valori numerici maggiori da una lista
« Risposta #6 il: Novembre 13, 2021, 18:12 »
questo è proprio quello che cercavo di fare, grazie infinite nuzzopippo! Per l'ennesima volta ho capito che otre a lavorare sul linguaggio python devo lavorare molto anche sulla logica prima di tutto. Adesso posso studiare bene le tue linee di codice  :) Grazie ancora  :ok:

Offline PyAnam

  • python unicellularis
  • *
  • Post: 7
  • Punti reputazione: 0
    • Mostra profilo
Re:Individuazione di valori numerici maggiori da una lista
« Risposta #7 il: Novembre 27, 2021, 17:27 »
salve nuzzopippo, adesso che riesco ad ottenere i valori desiderati ho un problema per arrivare al vero scopo di tutto. Una volta ottenuti i due valori dovrei tracciare una retta di tendenza che passi tra questi due punti, e verificare che il punto più recente in ordine temporale (l’ultimo), oppure il secondo più recente (il penultimo), sia superiore alla retta di tendenza. A questo link https://drive.google.com/file/d/19Yz7oe0jAfA_CP7PypUl8WreWMlYbn5O/view?usp=sharing lascio un’immagine dove si nota che gli ultimi due punti del grafico sono in questo caso entrambi superiori alla retta di tendenza. Come detto prima basterebbe anche che uno soltanto dei due fosse superiore.
Ho provato a plottare il grafico ma non riesco a capire il mio errore. Qui sotto il codice che ho scritto:


import csv
with open('pyanam.csv', 'r') as f:
data = list(csv.DictReader(f, delimiter=','))

for r in data:
print(r)

def get_p_min_vertex(d):
    data = d[::-1]
    elements = []
    m_value = str(max([int(x['fee']) for x in data]))
    for i in range(len(data)):
        if data[i]['fee'] == m_value:
            p = data[i]
            elements.append(p)
            break
    sd = data[i + 1:]
    rapp = [(int(p['fee']) - int(x['fee'])) / (int(x['time']) - int(p['time'])) for x in sd]
    i_r = rapp.index(min(rapp))
    s = sd[i_r]
    elements.append(s)
    return elements

p, s = get_p_min_vertex(data)
print('Max ',p)
print('Min',s)

############################

import pandas as pd
import matplotlib.pyplot as plt

csv = pd.read_csv('pyanam.csv')
data = csv[['fee', 'time']]
x = data['time']
z = p['time'], s['time']
y = data['fee']
k = p['fee'], s['fee']

plt.plot(x,y,marker='o',color='blue', label = 'line 1')
plt.plot(z,k,marker='o',linestyle='-.',color='red', label = 'line 2')
plt.xlabel('TIME')
plt.ylabel('RSI VALUE')
plt.title('Grafico RSI')

plt.show()

Offline nuzzopippo

  • python sapiens
  • *****
  • Post: 501
  • Punti reputazione: 0
    • Mostra profilo
Re:Individuazione di valori numerici maggiori da una lista
« Risposta #8 il: Novembre 28, 2021, 07:29 »
ALT!

Qua ci troviamo di fronte ad un problema "serio" : manca l'obiettivo da raggiungersi.

Non può sussistere l'ultimo grafico da Te esposto utilizzando "get_p_min_vertex", dato che tale funzione individua quella che abbiamo indicato come "retta di minima pendenza", che andrebbe a giacere sull'ultimo punto del nuovo grafico.

Devi, per prima cosa, stabilire in maniera rigorosa e chiara cosa devi ottenere e con quali condizioni e dati, soltanto una volta stabilito questo può iniziarsi a ragionare, la approssimazione per tentativi ed errore non è ammissibile in programmazione, la logica procedurale (algoritmo se vuoi) deve essere univoca e rigorosamente determinata, senza alcuna ambiguità.

[Edit] rileggendo e riflettendo mi è venuto il dubbio che Tu non riesca a tracciare detta "retta di minima pendenza" nel tuo grafico ... se è questo il problema, allora, ovviamente, il discorso passa per l'equazione della retta passante per due punti trovati da applicarsi all'ampiezza del grafico in questione, gironzolando in rete si trovano soluzioni, comunque, magari un po' rozza ma ne ho adattata una che restituisce una funzione che per ogni ordinata del grafico calcola l'ascissa corrispondente sulla retta, prova questo Tuo codice integrato e vedi se è quello che Ti serve:
import csv
import pandas as pd
from matplotlib import pyplot as plt

with open('pyanam.csv', 'r') as f:
data = list(csv.DictReader(f, delimiter=','))

for r in data:
print(r)

def get_p_min_vertex(d):
    data = d[::-1]
    elements = []
    m_value = str(max([int(x['fee']) for x in data]))
    for i in range(len(data)):
        if data[i]['fee'] == m_value:
            p = data[i]
            elements.append(p)
            break
    sd = data[i + 1:]
    rapp = [(int(p['fee']) - int(x['fee'])) / (int(x['time']) - int(p['time'])) for x in sd]
    i_r = rapp.index(min(rapp))
    s = sd[i_r]
    elements.append(s)
    return elements

p, s = get_p_min_vertex(data)
print('Max ',p)
print('Min',s)

def crea_retta(x1,y1,x2,y2):
    if x1==x2 and y1==y2:
        raise ValueError("Infinite rette")
    elif x1==x2:
        raise ValueError("Equazione nella forma x=q")
    else:
        m=float(y2-y1)/(x2-x1)
        q=y1-(m*x1)
    def retta(x):
        return m*x+q
    return retta

############################

csv = pd.read_csv('pyanam.csv')
data = csv[['fee', 'time']]
print(data)
x = data['time']
y = data['fee']
plt.plot(x,y,marker='o',color='blue', label = 'line 1')

try:
    r = crea_retta(int(p['time']), int(p['fee']),
                   int(s['time']), int(s['fee']))
except ValueError as e:
    print(format(e))
    r = None
x_min = min(data['time'])
x_max = max(data['time'])

plt.plot([n for n in range(x_min, x_max)], [r(n) for n in range(x_min, x_max)],
         marker='o',linestyle='-.',color='red', label = 'line 2')
plt.xlabel('TIME')
plt.ylabel('RSI VALUE')
plt.title('Grafico RSI')

plt.show()
« Ultima modifica: Novembre 28, 2021, 09:12 da nuzzopippo »

Offline PyAnam

  • python unicellularis
  • *
  • Post: 7
  • Punti reputazione: 0
    • Mostra profilo
Re:Individuazione di valori numerici maggiori da una lista
« Risposta #9 il: Novembre 28, 2021, 18:14 »
grazie mille ancora nuzzopippo, ho modificato quanto da te fatto per fare in modo che non vengano considerati i primi due valori per l'individuazione della retta. Adesso mi manca solo di verificare se i primi due valori inizialmente scartati o anche solo 1 siano sopra la retta, perché di fatto lo scopo è quello di verificare se avviene una rottura della retta di tendenza.


import csv
import pandas as pd
from matplotlib import pyplot as plt

with open('pyanam.csv', 'r') as f:
    data = data = list(csv.DictReader(f, delimiter=','))
    rimuovi=data.pop(0)
    rimuovi2 = data.pop(0)
    data

for r in data:
print(r)

def get_p_min_vertex(d):
    data = d[::-1]
    elements = []
    m_value = str(max([int(x['fee']) for x in data]))
    for i in range(len(data)):
        if data[i]['fee'] == m_value:
            p = data[i]
            elements.append(p)
            break
    sd = data[i + 1:]
    rapp = [(int(p['fee']) - int(x['fee'])) / (int(x['time']) - int(p['time'])) for x in sd]
    i_r = rapp.index(min(rapp))
    s = sd[i_r]
    elements.append(s)
    return elements

p, s = get_p_min_vertex(data)
print('Max ',p)
print('Min',s)

def crea_retta(x1,y1,x2,y2):
    if x1==x2 and y1==y2:
        raise ValueError("Infinite rette")
    elif x1==x2:
        raise ValueError("Equazione nella forma x=q")
    else:
        m=float(y2-y1)/(x2-x1)
        q=y1-(m*x1)
    def retta(x):
        return m*x+q
    return retta

############################

csv = pd.read_csv('pyanam.csv')
data = csv[['fee', 'time']]

print(data)
x = data['time']
y = data['fee']
plt.plot(x,y,marker='o',color='blue', label = 'line 1')

try:
    r = crea_retta(int(p['time']), int(p['fee']),
                   int(s['time']), int(s['fee']))
except ValueError as e:
    print(format(e))
    r = None
x_min = min(data['time'])
x_max = max(data['time'])

plt.plot([n for n in range(x_min, x_max)], [r(n) for n in range(x_min, x_max)],
         marker='o',linestyle='-.',color='red', label = 'line 2')
plt.xlabel('TIME')
plt.ylabel('RSI VALUE')
plt.title('Grafico RSI')
print('questo è rimosso: ',rimuovi)
print('questo è rimosso: ', rimuovi2)
plt.show()