Pandas: заполнить отсутствующую дату информацией из других строк - PullRequest
0 голосов
/ 26 марта 2020

Предположим, у меня есть следующий pandas фрейм данных:

Date    Region  Country Cases   Deaths  Lat Long
2020-03-08  Northern Territory  Australia   27  49  -12.4634    130.8456
2020-03-09  Northern Territory  Australia   80  85  -12.4634    130.8456
2020-03-12  Northern Territory  Australia   35  73  -12.4634    130.8456
2020-03-08  Western Australia   Australia   48  20  -31.9505    115.8605
2020-03-09  Western Australia   Australia   70  12  -31.9505    115.8605
2020-03-10  Western Australia   Australia   66  95  -31.9505    115.8605
2020-03-11  Western Australia   Australia   31  38  -31.9505    115.8605
2020-03-12  Western Australia   Australia   40  83  -31.9505    115.8605

Мне нужно обновить фрейм данных с отсутствующими датами в Северной Террироте, 2020-3-10 и 2020-3-11. Тем не менее, я хочу использовать всю информацию, за исключением случаев и смертей. Например:

Date    Region  Country Cases   Deaths  Lat Long
2020-03-08  Northern Territory  Australia   27  49  -12.4634    130.8456
2020-03-09  Northern Territory  Australia   80  85  -12.4634    130.8456
2020-03-10  Northern Territory  Australia   0   0   -12.4634    130.8456
2020-03-11  Northern Territory  Australia   0   0   -12.4634    130.8456
2020-03-12  Northern Territory  Australia   35  73  -12.4634    130.8456
2020-03-08  Western Australia   Australia   48  20  -31.9505    115.8605
2020-03-09  Western Australia   Australia   70  12  -31.9505    115.8605
2020-03-10  Western Australia   Australia   66  95  -31.9505    115.8605
2020-03-11  Western Australia   Australia   31  38  -31.9505    115.8605
2020-03-12  Western Australia   Australia   40  83  -31.9505    115.8605

Единственный способ, которым я могу думать об этом, - это перебирать все комбинации дат и стран.

РЕДАКТИРОВАТЬ

Эфран, кажется, на правильном пути, но я не могу заставить его работать. Вот реальные данные, с которыми я работаю вместо игрушечного примера.

import pandas as pd

unique_group = ['province','country','county']
csbs_df = pd.read_csv(
        'https://jordansdatabucket.s3-us-west-2.amazonaws.com/covid19data/csbs_df.csv.gz', index_col=0)

csbs_df['Date'] = pd.to_datetime(csbs_df['Date'], infer_datetime_format=True)
new_df = (
    csbs_df.set_index('Date')
    .groupby(unique_group)
    .resample('D').first()
    .fillna(dict.fromkeys(['confirmed', 'deaths'], 0))
    .ffill()
    .reset_index(level=3)
    .reset_index(drop=True))
new_df.head()
Date    id  lat lon Timestamp   province    country_code    country county  confirmed   deaths  source  Date_text
0   2020-03-25  1094.0  32.534893   -86.642709  2020-03-25 00:00:00+00:00   Alabama US  US  Autauga 1.0 0.0 CSBS    03/25/20
1   2020-03-26  901.0   32.534893   -86.642709  2020-03-26 00:00:00+00:00   Alabama US  US  Autauga 4.0 0.0 CSBS    03/26/20
2   2020-03-24  991.0   30.735891   -87.723525  2020-03-24 00:00:00+00:00   Alabama US  US  Baldwin 3.0 0.0 CSBS    03/24/20
3   2020-03-25  1080.0  30.735891   -87.723525  2020-03-25 00:00:00+00:00   Alabama US  US  Baldwin 4.0 0.0 CSBS    03/25/20
4   2020-03-26  1139.0  30.735891   -87.723525  2020-03-26 16:52:00+00:00   Alabama US  US  Baldwin 4.0 0.0 CSBS    03/26/20

Вы можете видеть, что он не вставляет дневной сэмпл, как указано. Я не уверен, что не так.

Редактировать 2

Вот мое решение, основанное на Эрфане.

import pandas as pd

csbs_df = pd.read_csv(
        'https://jordansdatabucket.s3-us-west-2.amazonaws.com/covid19data/csbs_df.csv.gz', index_col=0)
date_range = pd.date_range(csbs_df['Date'].min(),csbs_df['Date'].max(),freq='1D')
unique_group = ['country','province','county']
gb = csbs_df.groupby(unique_group)
sub_dfs =[]
for g in gb.groups:
    sub_df = gb.get_group(g)
    sub_df = (
        sub_df.set_index('Date')
        .reindex(date_range)
        .fillna(dict.fromkeys(['confirmed', 'deaths'], 0))
        .bfill()
        .ffill()
        .reset_index()
        .rename({'index':'Date'},axis=1)
        .drop({'id':1},axis=1))
    sub_df['Date_text'] = sub_df['Date'].dt.strftime('%m/%d/%y')
    sub_df['Timestamp'] = pd.to_datetime(sub_df['Date'],utc=True)
    sub_dfs.append(sub_df)
all_concat = pd.concat(sub_dfs)
assert((all_concat.groupby(['province','country','county']).count() == 3).all().all())

1 Ответ

3 голосов
/ 26 марта 2020

Использование GroupBy.resample, ffill и fillna:

Идея в том, что мы хотим «заполнить» пропущенные пропуски дат для каждой группы Region и Country. Это называется пересчетом временных рядов.

Так вот почему мы используем GroupBy.resample вместо DataFrame.resample здесь. Еще больше fillna и ffill необходимо для заполнения данных в соответствии с вашей логикой c.

df['Date'] = pd.to_datetime(df['Date'], infer_datetime_format=True)
dfn = (
    df.set_index('Date')
    .groupby(['Region', 'Country'])
    .resample('D').first()
    .fillna(dict.fromkeys(['Cases', 'Deaths'], 0))
    .ffill()
    .reset_index(level=2)
    .reset_index(drop=True)
)

        Date              Region    Country  Cases  Deaths      Lat      Long
0 2020-03-08  Northern Territory  Australia   27.0    49.0 -12.4634  130.8456
1 2020-03-09  Northern Territory  Australia   80.0    85.0 -12.4634  130.8456
2 2020-03-10  Northern Territory  Australia    0.0     0.0 -12.4634  130.8456
3 2020-03-11  Northern Territory  Australia    0.0     0.0 -12.4634  130.8456
4 2020-03-12  Northern Territory  Australia   35.0    73.0 -12.4634  130.8456
5 2020-03-08   Western Australia  Australia   48.0    20.0 -31.9505  115.8605
6 2020-03-09   Western Australia  Australia   70.0    12.0 -31.9505  115.8605
7 2020-03-10   Western Australia  Australia   66.0    95.0 -31.9505  115.8605
8 2020-03-11   Western Australia  Australia   31.0    38.0 -31.9505  115.8605
9 2020-03-12   Western Australia  Australia   40.0    83.0 -31.9505  115.8605

Редактировать:

Действительно, не во всех местах есть та же дата начала и окончания, поэтому мы должны принять это во внимание, следующие работы:

csbs_df = pd.read_csv(
        'https://jordansdatabucket.s3-us-west-2.amazonaws.com/covid19data/csbs_df.csv.gz'
).iloc[:, 1:]

csbs_df['Date_text'] = pd.to_datetime(csbs_df['Date_text'])
date_range = pd.date_range(csbs_df['Date_text'].min(), csbs_df['Date_text'].max(), freq='D')

def reindex_dates(data, dates):
    data = data.reindex(dates).fillna(dict.fromkeys(['Cases', 'Deaths'], 0)).ffill().bfill()
    return data

dfn = (
    csbs_df.set_index('Date_text')
    .groupby('id').apply(lambda x: reindex_dates(x, date_range))
    .reset_index(level=0, drop=True)
    .reset_index()
    .rename(columns={'index': 'Date'})
)

print(dfn.head())


        Date   id        lat        lon                  Timestamp  \
0 2020-03-24  0.0  40.714550 -74.007140  2020-03-24 00:00:00+00:00   
1 2020-03-25  0.0  40.714550 -74.007140  2020-03-25 00:00:00+00:00   
2 2020-03-26  0.0  40.714550 -74.007140  2020-03-26 00:00:00+00:00   
3 2020-03-24  1.0  41.163198 -73.756063  2020-03-24 00:00:00+00:00   
4 2020-03-25  1.0  41.163198 -73.756063  2020-03-25 00:00:00+00:00   

         Date  province country_code country       county  confirmed  deaths  \
0  2020-03-24  New York           US      US     New York    13119.0   125.0   
1  2020-03-25  New York           US      US     New York    15597.0   192.0   
2  2020-03-26  New York           US      US     New York    20011.0   280.0   
3  2020-03-24  New York           US      US  Westchester     2894.0     0.0   
4  2020-03-25  New York           US      US  Westchester     3891.0     1.0   

  source  
0   CSBS  
1   CSBS  
2   CSBS  
3   CSBS  
4   CSBS  
...