Python en sliding window

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • utopiaBe
  • Registratie: Maart 2009
  • Niet online
Mijn vraag

Dit is een rookie vraag ongetwijfeld, maar ik zie het even niet. Bedoeling is dat je binnen een lijst over een sliding window van 3 een gemiddelde neemt van deze drie elementen en deze in een nieuwe lijst stopt. De lijst hoeft geen lengte te hebben die een meervoud van drie is. Mijn poging levert een - voor mij althans - vreemd resultaat op:

Mijn redenering was door de lijst te lopen, in stappen van 3 en dan het gemiddelde van die 3 elementen te nemen en in een lijst te plakken. Via de modulo zou ik dan de resterende elementen achteraan de lijst verzamelen en daar het gemiddelde van nemen, waarna ook wordt toegevoegd aan de lijst met gemiddelden.

Nu, als ik wandel door mijn lijst via een gewone for-loop, zie een resultaat dat ik verwacht (output staat bij voorbeeld). Steek ik diezelfde for-loop in mijn functie dan gooit die op het einde echter een lege lijst, waardoor ik mijn berekening die zou moeten volgen niet meer kan maken. Ik heb er even een print tussen gezet om het probleem te duiden (output staat er ook bij). Als ik de while loop unquote, krijg ik dan ook "avgs.append(sum(lijst[i]+lijst[i+1]+lijst[i+2])/3) builtins.TypeError: 'int' object is not iterable". Ik vermoed dat dit aan die lege lijst te wijten is...

Mocht iemand dit kunnen verduidelijken zou ik enorm dankbaar zijn. Geen huiswerk overigens. Probeer nog wat oefeningen te maken voor mijn examen...

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
lijst = [4,2,3,4,5,6,8,4]
for i in range(0,len(lijst),3):
    print(lijst[i])

#Levert wel degelijk 4 4 8

def avgSlide(lijst):
    avgs = []
    rest = len(lijst)%3 #0 if True, 1 False
    idx = 0
    for i in range(0,len(lijst),3):
        idx = i+2
        som = 0
        print(lijst[i])
        while idx < len(lijst):
           #Deze lijn werd aangepast (stond sum voor initieel :s)
            avgs.append((lijst[i]+lijst[i+1]+lijst[i+2])/3)
         if rest !=0:
              for i in range(1,rest+1):
                  som += lijst[-i]
              avgs.append(som/rest)
    return avgs

print(avgSlide([4,2,3,4,5,6,8,4]))

#Dit levert dus 4 4 8 []

...

Relevante software en hardware die ik gebruik
Python 3.10.5 op Macbook M1
...

Wat ik al gevonden of geprobeerd heb
Google en mijn frustratie laten toenemen...
...

[ Voor 3% gewijzigd door utopiaBe op 18-01-2023 17:16 ]

Alle reacties


Acties:
  • +2 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 00:23

Janoz

Moderator Devschuur®

!litemod

Kijk eerst eens naar deze code:
code:
1
avgs.append(  sum(  lijst[i] + lijst[i+1] + lijst[i+2]  ) / 3 )

Ik denk namelijk dat je hier 2 oplossingen door elkaar gebruikt hebt waardoor het nu niet meer werkt. Dit is in ieder geval wel waar de foutmelding vandaan komt.

TypeError: 'int' object is not iterable" betekent in normaal nederlands 'je geeft me een getal terwijl ik een lijstje verwacht'.


Zodra je dit probleem opgelost hebben kunnen we daarna kijken waarom je programma in een oneindige lus komt ;)

[ Voor 12% gewijzigd door Janoz op 18-01-2023 15:57 ]

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • utopiaBe
  • Registratie: Maart 2009
  • Niet online
Ik pas em even aan. sum en dan nog +: dat gaat inderdaad niet lukken :'(. Aaargh!


Oneindige loop -> k heb 'em. idx blijft in de loop uiteraard behouden, dus je zou nooit uit de while loop springen.

Bedankt. Je gaf slechts een subtiele hint, en kan niet geloven dat ik daar zo zat op te staren. Fixed :-).

Deze doet perfect wat ik wou:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def avgSlide(lijst):
    avgs = []
    rest = len(lijst)%3 #0 if True, 1 False
    idx = 0
    for i in range(0,len(lijst),3):
        idx = i+2
        som = 0
        #print(lijst[i])
        if idx < len(lijst):
            avgs.append((lijst[i]+lijst[i+1]+lijst[i+2])/3)
        elif rest !=0:
            for i in range(1,rest+1):
                som += lijst[-i]
            avgs.append(som/rest)
            rest=0
    return avgs

[ Voor 180% gewijzigd door utopiaBe op 18-01-2023 17:26 ]


Acties:
  • 0 Henk 'm!

  • Daos
  • Registratie: Oktober 2004
  • Niet online
Maar is het wel wat je moest maken? Normaal ga je bij een "sliding window" namelijk steeds 1 vooruit. In jouw voorbeeld met het gemiddelde van 3 items in [4,2,3,4,5,6,8,4] doe je dan deze berekeningen:

code:
1
2
3
4
5
6
avg([4,2,3])
avg([2,3,4])
avg([3,4,5])
avg([4,5,6])
avg([5,6,8])
avg([6,8,4])


Met als resultaat:
code:
1
[3,3,4,5,6.33,6]


Op sommige plekken op internet gaan ze zelfs nog een stapje verder en noemen ze een "sliding window" alleen een efficiënte implementatie hiervan. Met 3 maakt het niet veel uit, maar als je window 100 is, dan kan je in plaats van elke keer het gemiddelde van 100 items te berekenen ook het gemiddelde berekenen uit het vorige gemiddelde, het getal dat erbij is gekomen en het getal dat is verdwenen.

Acties:
  • 0 Henk 'm!

  • utopiaBe
  • Registratie: Maart 2009
  • Niet online
Heb ik ook gemerkt in mijn zoektocht, maar toch wel echt steeds 3 opschuiven in dit geval. Had er nog nette grafische weergave van:

Afbeeldingslocatie: https://tweakers.net/i/uE7_IJsiigZ7YLK6Ow2qObrSYdg=/full-fit-in/4000x4000/filters:no_upscale():fill(white):strip_exif()/f/image/fGbK4OREVveW1WVwVGZNO1Qe.png?f=user_large

Ben door wat losse vragen aan het gaan ter voorbereiding van een examen. Helaas zonder modeloplossing, maar het werkt. Vermoed dat ze op een examen nog veel creatiever met sliding windows kunnen omgaan... Deze variant zal ik helaas niet krijgen ;-).

[ Voor 34% gewijzigd door utopiaBe op 18-01-2023 22:41 ]


Acties:
  • +1 Henk 'm!

  • Raynman
  • Registratie: Augustus 2004
  • Laatst online: 00:47
utopiaBe schreef op woensdag 18 januari 2023 @ 22:39:
Helaas zonder modeloplossing, maar het werkt.
Nog even ter illustratie hoe het ook kan, zonder moeilijk te doen met die "rest":
Python:
1
2
3
4
5
6
def avgSlide(lijst):
    avgs = []
    for i in range(0, len(lijst), 3):
        deellijst = lijst[i:i+3]
        avgs.append(sum(deellijst) / len(deellijst))
    return avgs

Of met list comprehension/generator expression:
Python:
1
2
3
def avgSlide(lijst):
    deellijsten = (lijst[i:i+3] for i in range(0, len(lijst), 3))
    return [sum(l) / len(l) for l in deellijsten]

Acties:
  • 0 Henk 'm!

  • utopiaBe
  • Registratie: Maart 2009
  • Niet online
Nou, dat ziet er inderdaad een stuk strakker en beter uit...

Acties:
  • 0 Henk 'm!

  • 0xDEADBEEF
  • Registratie: December 2003
  • Niet online
Uit de losse pols.
Rust:
1
2
3
4
5
6
7
8
9
10
11
12
fn main() {
    let m = Vec::from([4,2,3,4,5,6,8,4]);
    let mut avgs: Vec<f64> = Vec::new();

    for w in m.windows(3) {
        let avg = (w[0] + w[1] + w[2]) as f64 / w.len() as f64;
        println!("w: {:?}", w);
        avgs.push(avg);
    }

    println!("avgs: {:?}", avgs);
}


code:
1
2
3
4
5
6
7
8
$ rustc main.rs ; ./main 
w: [4, 2, 3]
w: [2, 3, 4]
w: [3, 4, 5]
w: [4, 5, 6]
w: [5, 6, 8]
w: [6, 8, 4]
avgs: [3.0, 3.0, 4.0, 5.0, 6.333333333333333, 6.0]

[ Voor 41% gewijzigd door 0xDEADBEEF op 23-01-2023 00:27 ]

"Religion is an insult to human dignity. With or without it you would have good people doing good things and evil people doing evil things. But for good people to do evil things, that takes religion." - Steven Weinberg


Acties:
  • 0 Henk 'm!

  • utopiaBe
  • Registratie: Maart 2009
  • Niet online
Thanks, maar was ff met Python in de weer. Niettemin mooi om het ook in Rust even te zien.

Bedankt trouwens voor de hulp. Examen overleefd ondertussen (en met een mooie 19/20 gestrand, ondanks mijn geploeter in het begin...) Na jaren nog eens gaan studeren en moet er nog wat inkomen, maar komt wel goed mits wat vastlopen hier en daar... Soms wat trager dan ik zou willen (heb er dan ook fulltime baan naast die wat vraagt).
Pagina: 1