Bagaimana mengelompokkan satu kolom yang bias pada berbagai interval dari yang lain dalam panda?
Saya memiliki pd.DataFrame berikut:
source = pd.DataFrame([[0.99, 0.98, 0.93, 0.81, 0.85, 0.71, 0.7, 0.69, 0.68, 0.66],
[100, 12, 312, 23, 2, 12, 32, 21, 21, 21]]).T
Saya ingin mengubahnya secepat mungkin menjadi:
desired_result = pd.DataFrame([[0.9, 0.8, 0.7, 0.6], [424, 25, 44, 63]]).T
Dimana di atas saya mendefinisikan interval 0.1
yang saya terapkan ke kolom 0
ke source
dataframe dan menjumlahkan 1
kolom dari dataframe yang sama. Idenya adalah bahwa ini harus bekerja dengan interval yang berbeda.
Apa yang saya coba:
Saya berpikir untuk menggunakan
pd.cut
tetapi sepertinya bukan itu yang saya cari.Saya tahu bahwa jika saya menambahkan kolom baru
source
dengan nilai duplikat 0,9, 0,8, 0,7 dan 0,6 pada baris yang sesuai, maka saya dapat menggunakangroupby
kolom baru ini dan kemudiansum
, tetapi saya bertanya-tanya apakah ada cara yang lebih bersih dan lebih cepat untuk melakukan ini? mis. yang seperti ini:
interval = 0.1
source['ints'] = (source[0] / interval).astype(int)
result = source.groupby(source['ints']).sum().reset_index()
result
Namun hal di atas tidak akan berfungsi jika saya mengubah bentuk interval 0,1 menjadi 0,05 misalnya.
Bantuan apa pun akan dihargai.
Jawaban
Untuk kecepatan: selalu coba untuk membuat vektor semampu Anda, dan hindari apply
sebanyak mungkin.
Berikut adalah cara yang lebih cepat (kredit untuk @DavidErickson untuk sort=False
):
interval = 0.1
source.groupby(np.trunc(source[0] / interval) * interval, sort=False)[1].sum().reset_index()
# out:
0 1
0 0.9 424.0
1 0.8 25.0
2 0.7 12.0
3 0.6 95.0
Perbedaan kecepatan bisa sangat dramatis untuk yang besar df
.
Coba dengan 1 juta baris, dikelompokkan dalam 10K nampan:
source = pd.DataFrame(np.random.normal(scale=1000, size=(int(1e6), 2)))
%%timeit
# ... (as above)
26.7 ms ± 292 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Dengan sebagai apply
gantinya:
1.51 s ± 11 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
(50x lebih lambat).
Anda dapat menggunakan custom_round
fungsi yang saya buat 3 modifikasihttps://stackoverflow.com/a/40372261/6366770:
- Saya digunakan
np.floor
sebagai penggantiround
Anda ingin turun. - Ini mengacaukan nilai yang berada di "perbatasan" dari sebuah bin, jadi saya menambahkan
+ base/100
(jadi0.9
akan0.9 + .009 = 0.909
dan dibulatkan ke 0,9 bukannya salah ke 0,8), sehingga tepat di atas perbatasan dan membulatkan ke bawah dengan benar. Saya pikir ini akan melindungi Anda. Anda bisa melakukannya1 / 1000
agar aman. - Jawaban yang saya bagikan sedang mencari
int
, begitu dihapusint
, karena kita melihat pelampung yang membulat
source = pd.DataFrame(np.random.normal(scale=1000, size=(int(1e6), 2)))
def custom_round(x, y, base):
return source.groupby((base * np.floor((x + (base / 100)) / base)), sort=False)[y].sum()
%timeit custom_round(source[0], 1, .1)
89.8 ms ± 1.14 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
Di komputer saya, jawaban yang diterima lebih lambat:
102 ms ± 1.86 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)