Saat menggunakan panda library untuk menganalisis dataset kecil, yang ukurannya tidak melebihi 100 megabyte, kinerja jarang menjadi masalah. Tetapi ketika datang ke studi set data, ukuran yang dapat mencapai beberapa gigabytes, masalah kinerja dapat menyebabkan peningkatan yang signifikan dalam durasi analisis data dan bahkan dapat menyebabkan ketidakmampuan untuk melakukan analisis karena kurangnya memori.
Sementara alat-alat seperti Spark dapat secara efisien memproses set data besar (dari ratusan gigabyte hingga beberapa terabyte), untuk memanfaatkan sepenuhnya kemampuan mereka, Anda biasanya memerlukan perangkat keras yang cukup kuat dan mahal. Dan, dibandingkan dengan panda, panda tidak berbeda dalam set alat yang kaya untuk pembersihan, penelitian, dan analisis data berkualitas tinggi. Untuk kumpulan data berukuran sedang, yang terbaik adalah mencoba menggunakan panda lebih efisien, daripada beralih ke alat lain.

Dalam materi yang kami terjemahkan hari ini, kami akan berbicara tentang fitur-fitur bekerja dengan memori saat menggunakan panda, dan bagaimana cara mengurangi konsumsi memori hingga hampir 90% dengan hanya memilih tipe data yang sesuai yang disimpan dalam kolom struktur data tabel pada
DataFrame
.
Bekerja dengan data pada pertandingan baseball
Kami akan bekerja dengan data pada pertandingan bisbol Liga Utama yang dikumpulkan selama 130 tahun dan diambil dari
Retrosheet .
Awalnya, data ini disajikan sebagai 127 file CSV, tetapi kami menggabungkannya ke dalam satu set data menggunakan
csvkit dan menambahkan, sebagai baris pertama dari tabel yang dihasilkan, baris dengan nama kolom. Jika mau, Anda dapat mengunduh
versi data kami dan bereksperimen dengannya, membaca artikel.
Mari kita mulai dengan mengimpor dataset dan melihat lima baris pertamanya. Anda dapat menemukannya di tabel
ini , pada
lembar
.
import pandas as pd gl = pd.read_csv('game_logs.csv') gl.head()
Di bawah ini adalah informasi tentang kolom paling penting dari tabel dengan data ini. Jika Anda ingin membaca penjelasan untuk semua kolom, di
sini Anda dapat menemukan kamus data untuk seluruh kumpulan data.
date
- Tanggal permainan.v_name
- Nama tim tamu.v_league
- Liga tim tamu.h_name
- Nama tim tuan rumah.h_league
- Liga tim tuan rumah.v_score
- Poin dari tim tandang.h_score
- Poin dari tim tuan rumah.v_line_score
- Ringkasan poin-poin dari tim tamu, misalnya - 010000(10)00
.h_line_score
- Ringkasan poin-poin dari tim tuan rumah, misalnya - 010000(10)0X
.park_id
- Pengidentifikasi bidang tempat permainan dimainkan.attendance
- Jumlah pemirsa.
Untuk mengetahui informasi umum tentang objek
DataFrame
, Anda dapat menggunakan metode
DataFrame.info () . Berkat metode ini, Anda dapat mempelajari tentang ukuran suatu objek, tentang tipe data, dan tentang penggunaan memori.
Secara default, panda, demi menghemat waktu,
DataFrame
informasi perkiraan tentang penggunaan memori dari
DataFrame
. Kami tertarik pada informasi yang akurat, jadi kami akan mengatur parameter
memory_usage
ke
'deep'
.
gl.info(memory_usage='deep')
Berikut ini informasi yang berhasil kami dapatkan:
<class 'pandas.core.frame.DataFrame'> RangeIndex: 171907 entries, 0 to 171906 Columns: 161 entries, date to acquisition_info dtypes: float64(77), int64(6), object(78) memory usage: 861.6 MB
Ternyata, kami memiliki 171.907 baris dan 161 kolom. Perpustakaan panda secara otomatis mendeteksi tipe data. Ada 83 kolom dengan data numerik dan 78 kolom dengan objek. Kolom objek digunakan untuk menyimpan data string, dan dalam kasus di mana kolom berisi data dari tipe yang berbeda.
Sekarang, untuk lebih memahami bagaimana Anda dapat mengoptimalkan penggunaan memori dengan
DataFrame
ini, mari kita bicara tentang bagaimana panda menyimpan data dalam memori.
Tampilan Internal DataFrame
Di dalam panda, kolom data dikelompokkan ke dalam blok dengan nilai dari tipe yang sama. Berikut adalah contoh bagaimana 12 kolom pertama
DataFrame
disimpan dalam panda.
Representasi internal dari berbagai jenis data dalam pandaAnda mungkin memperhatikan bahwa blok tidak menyimpan informasi nama kolom. Ini disebabkan oleh fakta bahwa blok dioptimalkan untuk menyimpan nilai yang tersedia dalam sel tabel objek
DataFrame
. Kelas
BlockManager
bertanggung jawab untuk menyimpan informasi tentang korespondensi antara indeks baris dan kolom dari set data dan apa yang disimpan dalam blok dengan tipe data yang sama. Ini memainkan peran API yang menyediakan akses ke data dasar. Saat kami membaca, mengedit, atau menghapus nilai, kelas
DataFrame
berinteraksi dengan kelas
BlockManager
untuk mengubah permintaan kami menjadi panggilan fungsi dan metode.
Setiap tipe data memiliki kelas khusus dalam modul
pandas.core.internals
. Sebagai contoh, panda menggunakan kelas
ObjectBlock
untuk mewakili blok yang berisi kolom string, dan kelas
FloatBlock
untuk mewakili blok yang berisi kolom yang
FloatBlock
angka titik-mengambang. Untuk blok yang mewakili nilai numerik yang terlihat seperti bilangan bulat atau angka floating point, panda menggabungkan kolom dan menyimpannya sebagai
ndarray
data
ndarray
perpustakaan NumPy. Struktur data ini didasarkan pada larik C, nilainya disimpan dalam blok memori yang berkelanjutan. Berkat skema penyimpanan data ini, akses ke fragmen data sangat cepat.
Karena data dari berbagai jenis disimpan secara terpisah, kami memeriksa penggunaan memori dari berbagai jenis data. Mari kita mulai dengan penggunaan memori rata-rata untuk berbagai jenis data.
for dtype in ['float','int','object']: selected_dtype = gl.select_dtypes(include=[dtype]) mean_usage_b = selected_dtype.memory_usage(deep=True).mean() mean_usage_mb = mean_usage_b / 1024 ** 2 print("Average memory usage for {} columns: {:03.2f} MB".format(dtype,mean_usage_mb))
Akibatnya, ternyata indikator rata-rata untuk penggunaan memori untuk data dari berbagai jenis terlihat seperti ini:
Average memory usage for float columns: 1.29 MB Average memory usage for int columns: 1.12 MB Average memory usage for object columns: 9.53 MB
Informasi ini membuat kita mengerti bahwa sebagian besar memori dihabiskan untuk 78 kolom yang menyimpan nilai objek. Kita akan membicarakan lebih lanjut tentang ini nanti, tetapi sekarang mari kita pikirkan apakah kita dapat meningkatkan penggunaan memori dengan kolom yang menyimpan data numerik.
Subtipe
Seperti yang telah kami katakan, panda mewakili nilai numerik sebagai struktur data NumPy
ndarray
dan menyimpannya dalam blok memori yang berdekatan. Model penyimpanan data ini memungkinkan Anda menghemat memori dan dengan cepat mengakses nilai. Karena panda mewakili setiap nilai dari jenis yang sama menggunakan jumlah byte yang sama, dan struktur
ndarray
menyimpan informasi tentang jumlah nilai, panda dapat dengan cepat dan akurat menampilkan informasi tentang jumlah memori yang dikonsumsi oleh kolom yang menyimpan nilai numerik.
Banyak tipe data dalam panda memiliki banyak subtipe yang dapat menggunakan lebih sedikit byte untuk mewakili setiap nilai. Sebagai contoh, tipe
float
memiliki subtipe
float16
,
float32
dan
float64
. Angka dalam nama tipe menunjukkan jumlah bit yang digunakan subtipe untuk mewakili nilai. Misalnya, dalam subtipe yang baru saja terdaftar, 2, 4, 8, dan 16 byte digunakan masing-masing untuk penyimpanan data. Tabel berikut menunjukkan subtipe tipe data yang paling umum digunakan dalam panda.
Penggunaan memori, byte
| Nomor titik mengambang
| Integer
| Bilangan bulat tak bertanda
| Tanggal dan waktu
| Nilai Boolean
| Obyek
|
1
| | int8
| uint8
| | bool
| |
2
| float16
| int16
| uint16
| | | |
4
| float32
| int32
| uint32
| | | |
8
| float64
| int64
| uint64
| datetime64
| | |
Kapasitas memori variabel
| | | | | | objek
|
Nilai tipe
int8
menggunakan 1 byte (8 bit) untuk menyimpan angka dan dapat mewakili 256 nilai biner (daya 2 hingga 8). Ini berarti bahwa subtipe ini dapat digunakan untuk menyimpan nilai dalam kisaran dari -128 hingga 127 (termasuk 0).
Untuk memeriksa nilai minimum dan maksimum yang sesuai untuk penyimpanan menggunakan setiap subtipe integer, Anda dapat menggunakan metode
numpy.iinfo()
. Pertimbangkan sebuah contoh:
import numpy as np int_types = ["uint8", "int8", "int16"] for it in int_types: print(np.iinfo(it))
Dengan menjalankan kode ini, kami mendapatkan data berikut:
Machine parameters for uint8 --------------------------------------------------------------- min = 0 max = 255 --------------------------------------------------------------- Machine parameters for int8 --------------------------------------------------------------- min = -128 max = 127 --------------------------------------------------------------- Machine parameters for int16 --------------------------------------------------------------- min = -32768 max = 32767 ---------------------------------------------------------------
Di sini Anda dapat memperhatikan perbedaan antara jenis
uint
(unsigned integer) dan
int
(signed integer). Kedua jenis memiliki kapasitas yang sama, tetapi ketika menyimpan hanya nilai positif dalam kolom, jenis yang tidak ditandatangani memungkinkan penggunaan memori yang lebih efisien.
Optimalisasi penyimpanan data numerik menggunakan subtipe
Fungsi
pd.to_numeric()
dapat digunakan untuk menurunkan konversi tipe numerik. Untuk memilih kolom integer, kami menggunakan metode
DataFrame.select_dtypes()
, kemudian kami mengoptimalkannya dan membandingkan penggunaan memori sebelum dan sesudah optimasi.
# , , # , . def mem_usage(pandas_obj): if isinstance(pandas_obj,pd.DataFrame): usage_b = pandas_obj.memory_usage(deep=True).sum() else: # , DataFrame, Series usage_b = pandas_obj.memory_usage(deep=True) usage_mb = usage_b / 1024 ** 2 # return "{:03.2f} MB".format(usage_mb) gl_int = gl.select_dtypes(include=['int']) converted_int = gl_int.apply(pd.to_numeric,downcast='unsigned') print(mem_usage(gl_int)) print(mem_usage(converted_int)) compare_ints = pd.concat([gl_int.dtypes,converted_int.dtypes],axis=1) compare_ints.columns = ['before','after'] compare_ints.apply(pd.Series.value_counts)
Berikut ini adalah hasil dari studi tentang konsumsi memori:
7.87 MB
1.48 MB
| Untuk
| Setelah
|
uint8
| NaN
| 5.0
|
uint32
| NaN
| 1.0
|
int64
| 6.0
| NaN
|
Akibatnya, Anda dapat melihat penurunan penggunaan memori dari 7,9 menjadi 1,5 megabyte, yaitu - kami mengurangi konsumsi memori lebih dari 80%. Dampak keseluruhan dari pengoptimalan ini pada
DataFrame
asli, bagaimanapun, tidak terlalu kuat karena hanya memiliki sedikit kolom integer.
Mari kita lakukan hal yang sama dengan kolom yang berisi angka floating point.
gl_float = gl.select_dtypes(include=['float']) converted_float = gl_float.apply(pd.to_numeric,downcast='float') print(mem_usage(gl_float)) print(mem_usage(converted_float)) compare_floats = pd.concat([gl_float.dtypes,converted_float.dtypes],axis=1) compare_floats.columns = ['before','after'] compare_floats.apply(pd.Series.value_counts)
Hasilnya adalah sebagai berikut:
100.99 MB
50.49 MB
| Untuk
| Setelah
|
float32
| NaN
| 77.0
|
float64
| 77.0
| NaN
|
Akibatnya, semua kolom yang menyimpan angka floating point dengan tipe data
float64
sekarang menyimpan jumlah tipe
float32
, yang memberi kami pengurangan 50% dalam penggunaan memori.
Buat salinan
DataFrame
asli, gunakan kolom numerik yang dioptimalkan ini, bukan yang awalnya ada di dalamnya, dan lihat keseluruhan penggunaan memori setelah optimasi.
optimized_gl = gl.copy() optimized_gl[converted_int.columns] = converted_int optimized_gl[converted_float.columns] = converted_float print(mem_usage(gl)) print(mem_usage(optimized_gl))
Inilah yang kami dapatkan:
861.57 MB
804.69 MB
Meskipun kami secara signifikan mengurangi konsumsi memori dengan kolom yang menyimpan data numerik, secara umum, di seluruh
DataFrame
, konsumsi memori menurun hanya 7%. Optimalisasi penyimpanan jenis objek dapat menjadi sumber perbaikan situasi yang jauh lebih serius.
Sebelum kami melakukan optimasi ini, kami akan melihat lebih dekat bagaimana string disimpan dalam panda, dan membandingkannya dengan bagaimana angka disimpan di sini.
Perbandingan mekanisme untuk menyimpan angka dan string
Jenis
object
mewakili nilai menggunakan objek string Python. Ini sebagian karena NumPy tidak mendukung representasi nilai string yang hilang. Karena Python adalah bahasa tingkat tinggi yang diinterpretasikan, Python tidak menyediakan alat bagi programmer untuk mengatur bagaimana data disimpan dalam memori.
Keterbatasan ini mengarah pada fakta bahwa string tidak disimpan dalam fragmen memori yang berdekatan, representasi mereka dalam memori terfragmentasi. Ini mengarah pada peningkatan konsumsi memori dan perlambatan kecepatan bekerja dengan nilai-nilai string. Setiap elemen dalam kolom yang menyimpan tipe data objek, pada kenyataannya, adalah pointer yang berisi "alamat" di mana nilai sebenarnya terletak di memori.
Berikut ini adalah diagram berdasarkan bahan
ini yang membandingkan penyimpanan data numerik menggunakan tipe data NumPy dan menyimpan string menggunakan tipe data built-in Python.
Menyimpan data numerik dan stringDi sini Anda dapat mengingat bahwa dalam salah satu tabel di atas ditunjukkan bahwa jumlah variabel memori digunakan untuk menyimpan data tipe objek. Meskipun setiap pointer menempati 1 byte memori, setiap nilai string tertentu mengambil jumlah memori yang sama yang akan digunakan untuk menyimpan string tunggal dalam Python. Untuk mengkonfirmasi ini, kami akan menggunakan metode
sys.getsizeof()
. Pertama, lihat pada masing-masing baris, dan kemudian pada objek
Series
panda yang menyimpan data string.
Jadi, pertama kita periksa baris yang biasa:
from sys import getsizeof s1 = 'working out' s2 = 'memory usage for' s3 = 'strings in python is fun!' s4 = 'strings in python is fun!' for s in [s1, s2, s3, s4]: print(getsizeof(s))
Di sini, data penggunaan memori terlihat seperti ini:
60
65
74
74
Sekarang mari kita lihat bagaimana penggunaan string dalam objek
Series
terlihat:
obj_series = pd.Series(['working out', 'memory usage for', 'strings in python is fun!', 'strings in python is fun!']) obj_series.apply(getsizeof)
Di sini kita mendapatkan yang berikut:
0 60 1 65 2 74 3 74 dtype: int64
Di sini Anda dapat melihat bahwa ukuran garis yang disimpan dalam objek
Series
panda mirip dengan ukurannya saat bekerja dengannya dalam Python dan saat merepresentasikannya sebagai entitas terpisah.
Optimalisasi penyimpanan data jenis objek menggunakan variabel kategorikal
Variabel kategorikal muncul di versi panda 0.15. Jenis yang sesuai,
category
, menggunakan nilai integer dalam mekanisme internal, bukan nilai-nilai asli yang disimpan di kolom tabel. Panda menggunakan kamus terpisah yang mengatur korespondensi nilai integer dan awal. Pendekatan ini berguna ketika kolom berisi nilai-nilai dari kumpulan terbatas. Ketika data yang disimpan dalam kolom dikonversi ke jenis
category
, panda menggunakan subtipe
int
, yang memungkinkan penggunaan memori yang paling efisien dan mampu mewakili semua nilai unik yang ditemukan dalam kolom.
Sumber data dan data kategorikal menggunakan subtipe int8Untuk memahami dengan tepat di mana kita dapat menggunakan data kategorikal untuk mengurangi konsumsi memori, kami menemukan jumlah nilai unik dalam kolom yang menyimpan nilai tipe objek:
gl_obj = gl.select_dtypes(include=['object']).copy() gl_obj.describe()
Anda dapat menemukan apa yang kami miliki di tabel
ini , di lembar
.
Misalnya, di kolom
day_of_week
, yang merupakan hari di mana permainan dimainkan, ada nilai 171907. Di antara mereka, hanya 7 yang unik. Secara keseluruhan, pandangan sekilas pada laporan ini cukup untuk memahami bahwa beberapa nilai unik digunakan dalam banyak kolom untuk mewakili data sekitar 172.000 game.
Sebelum kita melakukan optimasi skala penuh, mari kita pilih satu kolom yang menyimpan data objek, setidaknya
day_of_week
, dan lihat apa yang terjadi di dalam program ketika dikonversi ke tipe kategorikal.
Seperti yang telah disebutkan, kolom ini hanya berisi 7 nilai unik. Untuk mengonversinya menjadi tipe kategorikal, kami menggunakan metode
.astype()
.
dow = gl_obj.day_of_week print(dow.head()) dow_cat = dow.astype('category') print(dow_cat.head())
Inilah yang kami dapatkan:
0 Thu 1 Fri 2 Sat 3 Mon 4 Tue Name: day_of_week, dtype: object 0 Thu 1 Fri 2 Sat 3 Mon 4 Tue Name: day_of_week, dtype: category Categories (7, object): [Fri, Mon, Sat, Sun, Thu, Tue, Wed]
Seperti yang Anda lihat, meskipun jenis kolom telah berubah, data yang tersimpan di dalamnya terlihat sama seperti sebelumnya. Sekarang mari kita lihat apa yang terjadi di dalam program.
Dalam kode berikut, kami menggunakan atribut
Series.cat.codes
untuk mengetahui nilai integer mana yang digunakan tipe
category
untuk mewakili setiap hari dalam seminggu:
dow_cat.head().cat.codes
Kami berhasil menemukan yang berikut:
0 4 1 0 2 2 3 1 4 5 dtype: int8
Di sini Anda dapat melihat bahwa setiap nilai unik diberi nilai integer, dan bahwa kolom tersebut sekarang bertipe
int8
. Tidak ada nilai yang hilang, tetapi jika itu yang terjadi, -1 akan digunakan untuk menunjukkan nilai-nilai tersebut.
Sekarang mari kita bandingkan konsumsi memori sebelum dan sesudah mengonversi kolom
day_of_week
ke jenis
category
.
print(mem_usage(dow)) print(mem_usage(dow_cat))
Inilah hasilnya:
9.84 MB
0.16 MB
Seperti yang Anda lihat, pada awalnya 9,84 megabita memori dikonsumsi, dan setelah optimasi hanya 0,16 megabita, yang berarti peningkatan 98% pada indikator ini. Harap perhatikan bahwa bekerja dengan kolom ini mungkin menunjukkan salah satu skenario optimisasi paling menguntungkan ketika hanya 7 nilai unik yang digunakan dalam kolom yang berisi sekitar 172.000 elemen.
Meskipun gagasan mengubah semua kolom ke tipe data ini terlihat menarik, sebelum melakukan ini, pertimbangkan efek samping negatif dari konversi semacam itu. Jadi, minus paling serius dari transformasi ini adalah ketidakmungkinan melakukan operasi aritmatika pada data kategorikal. Ini juga berlaku untuk operasi aritmatika biasa, dan penggunaan metode seperti
Series.min()
dan
Series.max()
tanpa terlebih dahulu mengonversi data ke tipe angka nyata.
Kita harus membatasi penggunaan tipe
category
terutama kolom yang menyimpan data
object
tipe, di mana kurang dari 50% dari nilai-nilai unik. Jika semua nilai dalam kolom adalah unik, maka menggunakan jenis
category
akan meningkatkan tingkat penggunaan memori. Ini disebabkan oleh fakta bahwa dalam memori Anda harus menyimpan, selain kode kategori numerik, nilai string asli. Rincian tentang pembatasan jenis
category
dapat ditemukan dalam
dokumentasi panda.
Mari kita buat satu loop yang berulang-ulang di atas semua kolom yang menyimpan data
object
tipe, cari tahu apakah jumlah nilai unik dalam kolom melebihi 50%, dan jika demikian, ubah mereka untuk mengetikkan
category
.
converted_obj = pd.DataFrame() for col in gl_obj.columns: num_unique_values = len(gl_obj[col].unique()) num_total_values = len(gl_obj[col]) if num_unique_values / num_total_values < 0.5: converted_obj.loc[:,col] = gl_obj[col].astype('category') else: converted_obj.loc[:,col] = gl_obj[col]
Sekarang bandingkan apa yang terjadi setelah optimasi dengan apa yang terjadi sebelumnya:
print(mem_usage(gl_obj)) print(mem_usage(converted_obj)) compare_obj = pd.concat([gl_obj.dtypes,converted_obj.dtypes],axis=1) compare_obj.columns = ['before','after'] compare_obj.apply(pd.Series.value_counts)
Kami mendapatkan yang berikut ini:
752.72 MB
51.67 MB
| Untuk
| Setelah
|
objek
| 78.0
| NaN
|
kategori
| NaN
| 78.0
|
category
, , , , , , , , .
, , ,
object
, 752 52 , 93%. , . , , , , 891 .
optimized_gl[converted_obj.columns] = converted_obj mem_usage(optimized_gl)
:
'103.64 MB'
. - . ,
datetime
, , , .
date = optimized_gl.date print(mem_usage(date)) date.head()
:
0.66 MB
:
0 18710504 1 18710505 2 18710506 3 18710508 4 18710509 Name: date, dtype: uint32
,
uint32
. -
datetime
, 64 .
datetime
, , , .
to_datetime()
,
format
,
YYYY-MM-DD
.
optimized_gl['date'] = pd.to_datetime(date,format='%Y%m%d') print(mem_usage(optimized_gl)) optimized_gl.date.head()
:
104.29 MB
:
0 1871-05-04 1 1871-05-05 2 1871-05-06 3 1871-05-08 4 1871-05-09 Name: date, dtype: datetime64[ns]
DataFrame
. , , , , , , , . , . , , , . , ,
DataFrame
, .
, .
pandas.read_csv() , . ,
dtype
, , , , — NumPy.
, , . , .
dtypes = optimized_gl.drop('date',axis=1).dtypes dtypes_col = dtypes.index dtypes_type = [i.name for i in dtypes.values] column_types = dict(zip(dtypes_col, dtypes_type)) # 161 , # 10 / # preview = first2pairs = {key:value for key,value in list(column_types.items())[:10]} import pprint pp = pp = pprint.PrettyPrinter(indent=4) pp.pprint(preview) : { 'acquisition_info': 'category', 'h_caught_stealing': 'float32', 'h_player_1_name': 'category', 'h_player_9_name': 'category', 'v_assists': 'float32', 'v_first_catcher_interference': 'float32', 'v_grounded_into_double': 'float32', 'v_player_1_id': 'category', 'v_player_3_id': 'category', 'v_player_5_id': 'category'}
, , .
- :
read_and_optimized = pd.read_csv('game_logs.csv',dtype=column_types,parse_dates=['date'],infer_datetime_format=True) print(mem_usage(read_and_optimized)) read_and_optimized.head()
:
104.28 MB
,
.
,
, , , . pandas 861.6 104.28 , 88% .
, , , . .
optimized_gl['year'] = optimized_gl.date.dt.year games_per_day = optimized_gl.pivot_table(index='year',columns='day_of_week',values='date',aggfunc=len) games_per_day = games_per_day.divide(games_per_day.sum(axis=1),axis=0) ax = games_per_day.plot(kind='area',stacked='true') ax.legend(loc='upper right') ax.set_ylim(0,1) plt.show()
,, 1920- , , 50 , .
, , , 50 , .
, .
game_lengths = optimized_gl.pivot_table(index='year', values='length_minutes') game_lengths.reset_index().plot.scatter('year','length_minutes') plt.show()
, 1940- .
Ringkasan
pandas, ,
DataFrame
, 90%. :
, , , , , , pandas, , .
Pembaca yang budiman! eugene_bb . - , — .
