Toon posts:

Python, pandas uurtotaal verdelen

Pagina: 1
Acties:

Vraag


Acties:
  • +1Henk 'm!

  • Fonkymunkey
  • Registratie: April 2007
  • Laatst online: 11-11 19:44
Hoi,

===============================
EDIT:
Ik heb het probleem gevonden maar ben nog wel benieuwd of er een andere, efficiëntere manier is om dit projectje aan te pakken in pandas. Mocht iemand een suggestie hebben, graag :)
===============================


Ik ben kort geleden begonnen met python en om te oefenen heb ik een klein project bedacht. Nee geen schoolopdracht maar echt uitdaging voor mezelf.

Wat gebruik ik:
Python 3.7, Pycharm, Jupyter notebook en de Pandas library

Wat heb ik:
- Weerdata voor elk uur voor 2011 en 1212
- Fietsdiefstallen over 2011-2012
- Berovingen in 2011-2012


Wat wil ik:
- Alle data combineren in één tabel/dataframe
- Verbanden zichtbaar maken


Ik ben nu bezig met het samenbrengen van de data en het is me bíjna gelukt. Maar ik maak ergens een denkfout, gebruik de verkeerde manier, zie iets over het hoofd... Het totaal aantal diefstallen wijkt uiteindelijk ongeveer 2000 af. Ik vraag me af wat er nu misgaat en hoe ik de uurtotalen misschien anders kan berekenen. Ik hoop dat hier iemand iets kan verhelderen.

Best een lange post geworden dus bedankt voor het lezen alvast :)

Mijn code en uitleg staan onderaan.

Ik heb de volgende dataframes:

Weerdata


Fietsdiefstallen:


Berovingen:


Hoe werkt mijn code:

- Ik heb een dataframe df_complete met één rij voor élk uur in 2011-2012 :
(2*365 + 1schrikkeldag) * 24 uur = 17544 uur

- Ik verdeel de fietsdiefstallen over de uren waarin ze plaatsvinden
Dus stel de diefstal is tussen 00:00 en 05:00, dat zal deze diefstal 1/5 bijdragen aan het uurtotaal van de uren waarin hij plaatsvindt. Ter verduidelijking:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    T1: theft 1, start= h0, end = h4, duration = 5 hours
    T2: theft 2, start= h1, end = h3, duration = 3 hours
    T3: theft 3, start= h4, end = h7, duration = 4 hours
    T4: theft 4, start= h7, end = h9, duration = 3 hours

         T1    T2/T4   T3     summed weights
    h0   .2                  .2
    h1   .2   .33            .53
    h2   .2   .33            .53
    h3   .2   .33            .53
    h4   .2           .25    .45
    h5                .25    .25
    h6               . 25    .25
    h7        .33     .25    .58
    h8        .33            .33
    h9        .33            .33


- Op basis van de datetime van de berovingen wordt één opgeteld bij het totaal voor het uur waarin hij plaatsvindt.


De code voor het combineren van de data:
df_complete : dataframe met alle data
df_bt : Bike Thefts
df_rb : RoBberries

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
27
28
29
30
31
32
# COMBINE DATA ===========================================

# set the datetime column as index
df_complete.set_index('datetime', inplace=True)

# THEFTS
# get start, end and weight for each theft and add the weight
# to corresponding range of rows in df_complete
for i in range(len(df_bt)):
    
    start = df_bt.loc[i, 'bt_start']
    # substract a timedelta of one hour from 'end' because in
    # df_complete.loc[start:end, 'thefts'] , 'end' is inclusive
    end = df_bt.loc[i, 'bt_end'] - pd.Timedelta(1, 'h')
    weight = df_bt.loc[i, 'weight']
    df_complete.loc[start:end, 'thefts'] += weight

# ROBBERIES
# get the datetime of each robbery in df_rb and add 1
# to the corresponding row in df_complete
for i in range(len(df_rb)):
    
    robb = df_rb.iloc[i, 0]
    df_complete.loc[robb, 'robberies'] += 1


df_complete.to_csv(r'results\complete.csv')

#df_complete.head(50)

Total = df_complete['thefts'].sum()
print (Total)


Ik weet dat het eigenlijk een no-no is om te loopen over een dataframe. Ik ken op dit moment de andere(?) opties niet.


Het resultaat:


Het verdelen van de overvallen gaat helemaal goed.

Wat het niet helemaal goed gaat:
- er zijn diefstallen met een einddatum in 2013 (ongeveer 30) maar ik krijg geen out of range error bij het toevoegen
- De som van alle waarden uit de kolom 'thefts' komt op 14309 terwijl het aantal diefstallen 16177 is. Doordat er een klein deel in 2013 eindigt zal de som wat lager uitvallen maar 2000 lijkt me veel te veel.

Ik gok dat er iets niet klopt in dit stukje code:
code:
1
2
3
4
5
6
7
8
for i in range(len(df_bt)):
    
    start = df_bt.loc[i, 'bt_start']
    # substract a timedelta of one hour from 'end' because in
    # df_complete.loc[start:end, 'thefts'] , 'end' is inclusive
    end = df_bt.loc[i, 'bt_end'] - pd.Timedelta(1, 'h')
    weight = df_bt.loc[i, 'weight']
    df_complete.loc[start:end, 'thefts'] += weight

[Voor 8% gewijzigd door Fonkymunkey op 25-05-2020 17:34. Reden: info toegevoegd]

Beste antwoord (via Fonkymunkey op 13-06-2020 19:24)


  • Morrar
  • Registratie: Juni 2002
  • Laatst online: 08:36
Voor de berovingen kun je gewoon een aggregatie doen op de datetime toch en daarna joinen:

Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import pandas as pd

# Wat voorbeeld data in hetzelfde formaat
df_rb = pd.DataFrame({
  "datetime": ["2020-01-01 18:00", "2020-01-01 18:00", "2020-01-01 19:00"]
}).set_index("datetime")

# Vul 1 beroving in op elke rij
df_rb["robberies"] = 1

# Bereken het totaal per datetime (er van uitgaande dat dit de index is)
df_rb = df_rb.groupby(level=0).agg({"robberies": sum})

# En dan mergen met de rest... (join gebruikt de datetime index)
df_complete = df_complete.join(df_rb)

# Optioneel: vul nul in voor de ontbrekende waardes
df_complete = df_complete.fillna({"robberies": 0})


Voorkomt alvast een for loop.

De fietsendiefstallen zijn wat lastiger, omdat je dan eigenlijk een "between" join wilt doen die pandas niet kent (itt SQL)... Wat je zou kunnen doen is eerst de records maken per diefstal en deze dan optellen:

Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import pandas as pd

# Voorbeeld data
df = pd.DataFrame({
  "dt_start": ["2020-01-01 18:00", "2020-01-01 19:00"],
  "duration": [2, 3],
  "weight": [0.5, 0.33],
})

# Functie die dataframe per diefstal maakt
def explode(row):
  return pd.DataFrame({
    "datetime": pd.date_range(start=row["dt_start"], periods=row["duration"], freq="H"),
    "theft": row["weight"] / row["duration"]
  })

# Explode alle rijen en voeg ze weer samen tot 1 data frame
df = pd.concat([
  explode(row) for _, row in df.iterrows()
])

# Aggregeer per uur zodat je kunt joinen
df = df.groupby("datetime").agg({"theft": sum})


Zit nog steeds een for loop in, maar wellicht is het wat leesbaarder. Valt eventueel ook vrij makkelijk te paralleliseren zo trouwens.


P.S. Mijn voorbeeldjes gebruiken strings ipv datetime maar idee is hetzelfde.

[Voor 63% gewijzigd door Morrar op 25-05-2020 19:15]

Alle reacties


Acties:
  • +1Henk 'm!

  • Fonkymunkey
  • Registratie: April 2007
  • Laatst online: 11-11 19:44
Bedankt voor de input !
Bij deze nog even de resultaten:

De correlatie heatmap


Verband tussen temperatuur en fietsdiestal (temp = blauw, diefstal = oranje)
Pagina: 1



Google Pixel 7 Sony WH-1000XM5 Apple iPhone 14 Samsung Galaxy Watch5, 44mm Sonic Frontiers Samsung Galaxy Z Fold4 Insta360 X3 Nintendo Switch Lite

Tweakers is samen met Hardware Info, AutoTrack, Gaspedaal.nl, Nationale Vacaturebank, Intermediair en Independer onderdeel van DPG Media B.V.
Alle rechten voorbehouden © 1998 - 2022 Hosting door True

Tweakers maakt gebruik van cookies

Tweakers plaatst functionele en analytische cookies voor het functioneren van de website en het verbeteren van de website-ervaring. Deze cookies zijn noodzakelijk. Om op Tweakers relevantere advertenties te tonen en om ingesloten content van derden te tonen (bijvoorbeeld video's), vragen we je toestemming. Via ingesloten content kunnen derde partijen diensten leveren en verbeteren, bezoekersstatistieken bijhouden, gepersonaliseerde content tonen, gerichte advertenties tonen en gebruikersprofielen opbouwen. Hiervoor worden apparaatgegevens, IP-adres, geolocatie en surfgedrag vastgelegd.

Meer informatie vind je in ons cookiebeleid.

Sluiten

Toestemming beheren

Hieronder kun je per doeleinde of partij toestemming geven of intrekken. Meer informatie vind je in ons cookiebeleid.

Functioneel en analytisch

Deze cookies zijn noodzakelijk voor het functioneren van de website en het verbeteren van de website-ervaring. Klik op het informatie-icoon voor meer informatie. Meer details

janee

    Relevantere advertenties

    Dit beperkt het aantal keer dat dezelfde advertentie getoond wordt (frequency capping) en maakt het mogelijk om binnen Tweakers contextuele advertenties te tonen op basis van pagina's die je hebt bezocht. Meer details

    Tweakers genereert een willekeurige unieke code als identifier. Deze data wordt niet gedeeld met adverteerders of andere derde partijen en je kunt niet buiten Tweakers gevolgd worden. Indien je bent ingelogd, wordt deze identifier gekoppeld aan je account. Indien je niet bent ingelogd, wordt deze identifier gekoppeld aan je sessie die maximaal 4 maanden actief blijft. Je kunt deze toestemming te allen tijde intrekken.

    Ingesloten content van derden

    Deze cookies kunnen door derde partijen geplaatst worden via ingesloten content. Klik op het informatie-icoon voor meer informatie over de verwerkingsdoeleinden. Meer details

    janee