Seite 1 von 1

Zeitreihe CSV Tage nummerieren WENN Wert überschritten

Verfasst: Donnerstag 12. März 2020, 12:51
von mueller
Hallo zusammen,

ich möchte folgendes in Python umsetzen.
Ich lese eine CSV-Datei mit Datum, Land, und Anzahl Patienten. Vorsicht: Unsortiert und Lücken in den Daten (fehlt zwischendurch mal ein Tag)

Code: Alles auswählen

date,country,patients
2020-01-02,germany,0
2020-01-04,swiss,0
2020-01-06,germany,5
2020-01-01,germany,0
2020-01-03,germany,1
2020-01-05,swiss,0
2020-01-03,france,0
2020-01-07,swiss,5
2020-01-05,germany,4
2020-01-02,france,0
Nun möchte ich, dass für jedes Land der erste Tag ("Tag 1" erstes Mal ein Patient in diesem Land (>0) ) bestimmt wird, und alle nachfolgenden Tage entsprechend fortlaufend benannt sind (2,3,4,5,6, etc.).

Ergebnis etwa so: (ob sortiert oder unsortiert spielt keine Rolle)

Code: Alles auswählen

date,country,patients,day
2020-01-02,germany,0
2020-01-04,swiss,0
2020-01-06,germany,5,4
2020-01-01,germany,0
2020-01-03,germany,1,1
2020-01-05,swiss,0
2020-01-03,france,0
2020-01-07,swiss,5,1
2020-01-05,germany,4,3
2020-01-02,france,0
Bisher habe ich nur die CSV-Datei geladen

Code: Alles auswählen

import pandas as pd 

# Read data from file
data = pd.read_csv("data.csv") 

data.head(10)
Gibts dafür eine elegante Methode? Ich konnte bisher leider nichts finden.

Problematik aus meiner Sicht:
  • Sortieren der Daten nach Datum
  • Fortlaufende Tagesbezeichnung je Land
  • Das mitzählen nicht vorhandener Tage im Datensatz
Grüße
Jonas

Re: Zeitreihe CSV Tage nummerieren WENN Wert überschritten

Verfasst: Donnerstag 12. März 2020, 13:42
von sparrow
Ich kenne mich mit Pandas nicht aus.

Ich würde die Daten mit dem csv-Modul laden und in ein dictionary schreiben. [country][date] = patients.
date würde ich tasächlich in ein datetime.date wandeln. Das ist sortierbar und man kann die Differenz zum Startdatum daraus errechnen. Du sagtest ja, dass es Lücken geben kann - aber nicht ob die aufgefüllt werden sollen.

Re: Zeitreihe CSV Tage nummerieren WENN Wert überschritten

Verfasst: Donnerstag 12. März 2020, 14:02
von Sirius3
Man könnte erst alle Tage mit 0 Patienten herausfiltern, dann nach dem Land gruppieren, um dann die Tage zu sortieren und wieder aufzufüllen.

Re: Zeitreihe CSV Tage nummerieren WENN Wert überschritten

Verfasst: Donnerstag 12. März 2020, 14:04
von __blackjack__
@mueller: Das sieht so aus als wenn man nach Land und dann innerhalb der Länder nach Datum sortieren möchte und dann die Länder Gruppenweise bearbeiten möchte in dem man ein Delta in Tagen zum ersten Datum mit Patienten >0 in jedem Land als neue Spalte hinzufügt.

Re: Zeitreihe CSV Tage nummerieren WENN Wert überschritten

Verfasst: Donnerstag 12. März 2020, 14:41
von __blackjack__
Ansatz:

Code: Alles auswählen

#!/usr/bin/env python3
import io

import pandas as pd

DATA = b"""\
date,country,patients
2020-01-02,germany,0
2020-01-04,swiss,0
2020-01-06,germany,5
2020-01-01,germany,0
2020-01-03,germany,1
2020-01-05,swiss,0
2020-01-03,france,0
2020-01-07,swiss,5
2020-01-05,germany,4
2020-01-02,france,0
"""


def add_days(dataframe):
    dates = dataframe.index.get_level_values(1)
    return dataframe.assign(days=dates - dates[0] + pd.Timedelta(days=1))


def main():
    df = pd.read_csv(io.BytesIO(DATA), sep=",", parse_dates=[0])
    df = df.set_index(["country", "date"]).sort_index()
    print(df)
    result_df = df[df["patients"] > 0].groupby(level=0).apply(add_days)
    print(result_df)


if __name__ == "__main__":
    main()

Re: Zeitreihe CSV Tage nummerieren WENN Wert überschritten

Verfasst: Freitag 13. März 2020, 14:15
von mueller
__blackjack__ hat geschrieben: Donnerstag 12. März 2020, 14:41 Ansatz:

Code: Alles auswählen

#!/usr/bin/env python3
import io

import pandas as pd

DATA = b"""\
date,country,patients
2020-01-02,germany,0
2020-01-04,swiss,0
2020-01-06,germany,5
2020-01-01,germany,0
2020-01-03,germany,1
2020-01-05,swiss,0
2020-01-03,france,0
2020-01-07,swiss,5
2020-01-05,germany,4
2020-01-02,france,0
"""


def add_days(dataframe):
    dates = dataframe.index.get_level_values(1)
    return dataframe.assign(days=dates - dates[0] + pd.Timedelta(days=1))


def main():
    df = pd.read_csv(io.BytesIO(DATA), sep=",", parse_dates=[0])
    df = df.set_index(["country", "date"]).sort_index()
    print(df)
    result_df = df[df["patients"] > 0].groupby(level=0).apply(add_days)
    print(result_df)


if __name__ == "__main__":
    main()
Das sieht sehr gut aus, vielen Dank!

Re: Zeitreihe CSV Tage nummerieren WENN Wert überschritten

Verfasst: Samstag 14. März 2020, 22:20
von mueller
Ich muss euch nochmals behelligen.

Also: Wenn ich ne CSV-Datei aufrufe, klappt es leider nicht:

Code: Alles auswählen

#!/usr/bin/env python3
import io

import pandas as pd

DATA = pd.read_csv ('test2.csv', parse_dates=True)

print(DATA.head())

def add_days(dataframe):
    print("add_days started")
    dates = dataframe.index.get_level_values(1)
    return dataframe.assign(days=dates - dates[0] + pd.Timedelta(days=1))



#df = pd.read_csv(io.StringIO(DATA), sep=",", parse_dates=[0])
df = DATA.set_index(["country", "date"]).sort_index()
print(df)
result_df = df[df["patients"] > 0].groupby(level=0).apply(add_days)
print(result_df)
Er scheint mit den Days / dates nicht rechnen zu können, weil er die als String einliest?

OUTPUT:

Code: Alles auswählen

         date  country  patients
0  2020-01-02  germany         0
1  2020-01-04    swiss         0
2  2020-01-06  germany         5
3  2020-01-01  germany         0
4  2020-01-03  germany         1
                    patients
country date                
france  2020-01-02         0
        2020-01-03         0
germany 2020-01-01         0
        2020-01-02         0
        2020-01-03         1
        2020-01-05         4
        2020-01-06         5
swiss   2020-01-04         0
        2020-01-05         0
        2020-01-07         5
add_days started
add_days started

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
~\Anaconda3\lib\site-packages\pandas\core\groupby\groupby.py in apply(self, func, *args, **kwargs)
    688             try:
--> 689                 result = self._python_apply_general(f)
    690             except Exception:

~\Anaconda3\lib\site-packages\pandas\core\groupby\groupby.py in _python_apply_general(self, f)
    706         keys, values, mutated = self.grouper.apply(f, self._selected_obj,
--> 707                                                    self.axis)
    708 

~\Anaconda3\lib\site-packages\pandas\core\groupby\ops.py in apply(self, f, data, axis)
    189             group_axes = _get_axes(group)
--> 190             res = f(group)
    191             if not _is_indexed_like(res, group_axes):

<ipython-input-12-d27a719bceb9> in add_days(dataframe)
     12     dates = dataframe.index.get_level_values(1)
---> 13     return dataframe.assign(days=dates - dates[0] + pd.Timedelta(days=1))
     14 

~\Anaconda3\lib\site-packages\pandas\core\indexes\base.py in __sub__(self, other)
   2213     def __sub__(self, other):
-> 2214         return Index(np.array(self) - other)
   2215 

TypeError: unsupported operand type(s) for -: 'str' and 'str'

During handling of the above exception, another exception occurred:

TypeError                                 Traceback (most recent call last)
<ipython-input-12-d27a719bceb9> in <module>
     18 df = DATA.set_index(["country", "date"]).sort_index()
     19 print(df)
---> 20 result_df = df[df["patients"] > 0].groupby(level=0).apply(add_days)
     21 print(result_df)

~\Anaconda3\lib\site-packages\pandas\core\groupby\groupby.py in apply(self, func, *args, **kwargs)
    699 
    700                 with _group_selection_context(self):
--> 701                     return self._python_apply_general(f)
    702 
    703         return result

~\Anaconda3\lib\site-packages\pandas\core\groupby\groupby.py in _python_apply_general(self, f)
    705     def _python_apply_general(self, f):
    706         keys, values, mutated = self.grouper.apply(f, self._selected_obj,
--> 707                                                    self.axis)
    708 
    709         return self._wrap_applied_output(

~\Anaconda3\lib\site-packages\pandas\core\groupby\ops.py in apply(self, f, data, axis)
    188             # group might be modified
    189             group_axes = _get_axes(group)
--> 190             res = f(group)
    191             if not _is_indexed_like(res, group_axes):
    192                 mutated = True

<ipython-input-12-d27a719bceb9> in add_days(dataframe)
     11     print("add_days started")
     12     dates = dataframe.index.get_level_values(1)
---> 13     return dataframe.assign(days=dates - dates[0] + pd.Timedelta(days=1))
     14 
     15 

~\Anaconda3\lib\site-packages\pandas\core\indexes\base.py in __sub__(self, other)
   2212 
   2213     def __sub__(self, other):
-> 2214         return Index(np.array(self) - other)
   2215 
   2216     def __rsub__(self, other):

TypeError: unsupported operand type(s) for -: 'str' and 'str'

Ist vermutlich ein kleines Problem, aber ich finde keine Lösung.

Grüße
Jonas

Re: Zeitreihe CSV Tage nummerieren WENN Wert überschritten

Verfasst: Samstag 14. März 2020, 22:30
von sparrow
Laut Dokumentation kennt pandas.rasd_csv den Parameter dtype um den Typ einer Spalte anzugeben.

Re: Zeitreihe CSV Tage nummerieren WENN Wert überschritten

Verfasst: Samstag 14. März 2020, 22:54
von mueller
sparrow hat geschrieben: Samstag 14. März 2020, 22:30 Laut Dokumentation kennt pandas.rasd_csv den Parameter dtype um den Typ einer Spalte anzugeben.
Spuckt folgendes aus:

Code: Alles auswählen

date        object
country     object
patients     int64
dtype: object

Re: Zeitreihe CSV Tage nummerieren WENN Wert überschritten

Verfasst: Samstag 14. März 2020, 22:56
von sparrow
Dass es den falschen Typ erkennt hast du ja schon vorher festgestellt und nun bestätigt. Du sollst den Parameter nutzen um den korrekten dtype zu setzen.

Re: Zeitreihe CSV Tage nummerieren WENN Wert überschritten

Verfasst: Samstag 14. März 2020, 23:04
von mueller
sparrow hat geschrieben: Samstag 14. März 2020, 22:56 Dass es den falschen Typ erkennt hast du ja schon vorher festgestellt und nun bestätigt. Du sollst den Parameter nutzen um den korrekten dtype zu setzen.
Bist n Guter!
Ich stand aufm Schlauch. Jetzt gehts.

Code: Alles auswählen

#!/usr/bin/env python3
import io

import pandas as pd

DATA = pd.read_csv ('test2.csv', parse_dates=True)

#print(DATA.head())

DATA['date'] = DATA['date'].astype('datetime64')

def add_days(dataframe):
    print("add_days started")
    dates = dataframe.index.get_level_values(1)
    return dataframe.assign(days=dates - dates[0] + pd.Timedelta(days=1))

df = DATA.set_index(["country", "date"]).sort_index()
print(df)
result_df = df[df["patients"] > 0].groupby(level=0).apply(add_days)
print(result_df)