Saya memiliki kerangka data yang terlihat seperti ini

ID |   START  |   END
1  |2016-12-31|2017-02-30
2  |2017-01-30|2017-10-30
3  |2016-12-21|2018-12-30

Saya ingin tahu jumlah ID aktif di setiap hari yang memungkinkan. Jadi pada dasarnya menghitung jumlah periode waktu yang tumpang tindih.

Apa yang saya lakukan untuk menghitung ini adalah membuat bingkai data baru c_df dengan tanggal kolom dan jumlah. Kolom pertama diisi menggunakan rentang:

all_dates = pd.date_range(start=min(df['START']), end=max(df['END']))

Kemudian untuk setiap baris dalam bingkai data asli saya, saya menghitung rentang yang berbeda untuk tanggal mulai dan akhir:

id_dates = pd.date_range(start=min(user['START']), end=max(user['END']))

Saya kemudian menggunakan rentang tanggal ini untuk menambah satu sel hitungan yang sesuai di c_df.

Semua loop ini tidak terlalu efisien untuk kumpulan data besar dan terlihat jelek. Apakah ada cara yang lebih efisien untuk melakukan ini?

1
dearn44 29 Januari 2020, 20:25

2 jawaban

Jawaban Terbaik

Jika kerangka data Anda cukup kecil sehingga kinerja tidak menjadi masalah, buat rentang tanggal untuk setiap baris, lalu ledakkan dan hitung berapa kali setiap tanggal ada dalam rangkaian yang diledakkan.

Membutuhkan panda >= 0.25:

df.apply(lambda row: pd.date_range(row['START'], row['END']), axis=1) \
    .explode() \
    .value_counts() \
    .sort_index()

Jika kerangka data Anda besar, manfaatkan penyiaran numpy untuk meningkatkan kinerja.

Bekerja dengan versi panda apa pun:

dates = pd.date_range(df['START'].min(), df['END'].max()).values
start = df['START'].values[:, None]
end = df['END'].values[:, None]

mask = (start <= dates) & (dates <= end)
result = pd.DataFrame({
    'Date': dates,
    'Count': mask.sum(axis=0)
})
1
Code Different 29 Januari 2020, 17:59

Buat IntervalIndex dan gunakan pemahaman genex atau daftar dengan contains untuk memeriksa setiap tanggal lagi setiap interval (Catatan: Saya membuat sampel yang lebih kecil untuk menguji solusi ini)

Sample `df`

Out[56]:
   ID      START        END
0   1 2016-12-31 2017-01-20
1   2 2017-01-20 2017-01-30
2   3 2016-12-28 2017-02-03
3   4 2017-01-20 2017-01-25

iix = pd.IntervalIndex.from_arrays(df.START, df.END, closed='both')
all_dates = pd.date_range(start=min(df['START']), end=max(df['END']))

df_final = pd.DataFrame({'dates': all_dates, 
                         'date_counts': (iix.contains(dt).sum() for dt in all_dates)})

In [58]: df_final
Out[58]:
        dates  date_counts
0  2016-12-28            1
1  2016-12-29            1
2  2016-12-30            1
3  2016-12-31            2
4  2017-01-01            2
5  2017-01-02            2
6  2017-01-03            2
7  2017-01-04            2
8  2017-01-05            2
9  2017-01-06            2
10 2017-01-07            2
11 2017-01-08            2
12 2017-01-09            2
13 2017-01-10            2
14 2017-01-11            2
15 2017-01-12            2
16 2017-01-13            2
17 2017-01-14            2
18 2017-01-15            2
19 2017-01-16            2
20 2017-01-17            2
21 2017-01-18            2
22 2017-01-19            2
23 2017-01-20            4
24 2017-01-21            3
25 2017-01-22            3
26 2017-01-23            3
27 2017-01-24            3
28 2017-01-25            3
29 2017-01-26            2
30 2017-01-27            2
31 2017-01-28            2
32 2017-01-29            2
33 2017-01-30            2
34 2017-01-31            1
35 2017-02-01            1
36 2017-02-02            1
37 2017-02-03            1
1
Andy L. 29 Januari 2020, 17:57