0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

pandas的內(nèi)存使用以及選擇合適的數(shù)據(jù)類(lèi)型

阿銘linux ? 來(lái)源:機(jī)器之心 ? 作者:Josh Devlin ? 2021-08-17 09:27 ? 次閱讀

選自DATAQUEST

作者:Josh Devlin

機(jī)器之心編譯

當(dāng)使用 pandas 操作小規(guī)模數(shù)據(jù)(低于 100 MB)時(shí),性能一般不是問(wèn)題。而當(dāng)面對(duì)更大規(guī)模的數(shù)據(jù)(100 MB 到數(shù) GB)時(shí),性能問(wèn)題會(huì)讓運(yùn)行時(shí)間變得更漫長(zhǎng),而且會(huì)因?yàn)閮?nèi)存不足導(dǎo)致運(yùn)行完全失敗。

盡管 Spark 這樣的工具可以處理大型數(shù)據(jù)集(100 GB 到數(shù) TB),但要完全利用它們的能力,往往需要更加昂貴的硬件。而且和 pandas 不同,它們?nèi)鄙儇S富的用于高質(zhì)量數(shù)據(jù)清理、探索和分析的功能集。對(duì)于中等規(guī)模的數(shù)據(jù),我們最好能更充分地利用 pandas,而不是換成另一種工具。

在這篇文章中,我們將了解 pandas 的內(nèi)存使用,以及如何只需通過(guò)為列選擇合適的數(shù)據(jù)類(lèi)型就能將 dataframe 的內(nèi)存占用減少近 90%。

處理棒球比賽日志

我們將處理 130 年之久的美國(guó)職業(yè)棒球大聯(lián)盟(MLB)比賽數(shù)據(jù),這些數(shù)據(jù)來(lái)自 Retrosheet:http://www.retrosheet.org/gamelogs/index.html。

這些數(shù)據(jù)原來(lái)分成了 127 個(gè)不同的 CSV 文件,但我們已經(jīng)使用 csvkit 合并了這些數(shù)據(jù),并在第一行增加了列名稱。如果你想下載本文所用的這個(gè)數(shù)據(jù)版本,請(qǐng)?jiān)L問(wèn):https://data.world/dataquest/mlb-game-logs。

讓我們首先導(dǎo)入數(shù)據(jù),并看看其中的前五行:

import pandas as pd

gl = pd.read_csv(‘game_logs.csv’)

gl.head()

下面我們總結(jié)了一些重要的列,但如果你想了解所有的列,我們也為整個(gè)數(shù)據(jù)集創(chuàng)建了一個(gè)數(shù)據(jù)詞典:https://data.world/dataquest/mlb-game-logs/workspace/data-dictionary。

date - 比賽時(shí)間

v_name - 客隊(duì)名

v_league - 客隊(duì)聯(lián)盟

h_name - 主隊(duì)名

h_league - 主隊(duì)聯(lián)盟

v_score - 客隊(duì)得分

h_score - 主隊(duì)得分

v_line_score - 客隊(duì)每局得分排列,例如:010000(10)00.

h_line_score - 主隊(duì)每局得分排列,例如:010000(10)0X.

park_id - 比賽舉辦的球場(chǎng)名

attendance- 比賽觀眾

我們可以使用 DataFrame.info() 方法為我們提供關(guān)于 dataframe 的高層面信息,包括它的大小、數(shù)據(jù)類(lèi)型的信息和內(nèi)存使用情況。

默認(rèn)情況下,pandas 會(huì)近似 dataframe 的內(nèi)存用量以節(jié)省時(shí)間。因?yàn)槲覀円碴P(guān)心準(zhǔn)確度,所以我們將 memory_usage 參數(shù)設(shè)置為 ‘deep’,以便得到準(zhǔn)確的數(shù)字。

gl.info(memory_usage=‘deep’)

《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

我們可以看到,我們有 171,907 行和 161 列。pandas 會(huì)自動(dòng)為我們檢測(cè)數(shù)據(jù)類(lèi)型,發(fā)現(xiàn)其中有 83 列數(shù)據(jù)是數(shù)值,78 列是 object。object 是指有字符串或包含混合數(shù)據(jù)類(lèi)型的情況。

為了更好地理解如何減少內(nèi)存用量,讓我們看看 pandas 是如何將數(shù)據(jù)存儲(chǔ)在內(nèi)存中的。

dataframe 的內(nèi)部表示

在 pandas 內(nèi)部,同樣數(shù)據(jù)類(lèi)型的列會(huì)組織成同一個(gè)值塊(blocks of values)。這里給出了一個(gè)示例,說(shuō)明了 pandas 對(duì)我們的 dataframe 的前 12 列的存儲(chǔ)方式。

你可以看到這些塊并沒(méi)有保留原有的列名稱。這是因?yàn)檫@些塊為存儲(chǔ) dataframe 中的實(shí)際值進(jìn)行了優(yōu)化。pandas 的 BlockManager 類(lèi)則負(fù)責(zé)保留行列索引與實(shí)際塊之間的映射關(guān)系。它可以作為一個(gè) API 使用,提供了對(duì)底層數(shù)據(jù)的訪問(wèn)。不管我們何時(shí)選擇、編輯或刪除這些值,dataframe 類(lèi)和 BlockManager 類(lèi)的接口都會(huì)將我們的請(qǐng)求翻譯成函數(shù)和方法的調(diào)用。

在 pandas.core.internals 模塊中,每一種類(lèi)型都有一個(gè)專門(mén)的類(lèi)。pandas 使用 ObjectBlock 類(lèi)來(lái)表示包含字符串列的塊,用 FloatBlock 類(lèi)表示包含浮點(diǎn)數(shù)列的塊。對(duì)于表示整型數(shù)和浮點(diǎn)數(shù)這些數(shù)值的塊,pandas 會(huì)將這些列組合起來(lái),存儲(chǔ)成 NumPy ndarray。NumPy ndarray 是圍繞 C 語(yǔ)言的數(shù)組構(gòu)建的,其中的值存儲(chǔ)在內(nèi)存的連續(xù)塊中。這種存儲(chǔ)方案使得對(duì)值的訪問(wèn)速度非??臁?/p>

因?yàn)槊糠N數(shù)據(jù)類(lèi)型都是分開(kāi)存儲(chǔ)的,所以我們將檢查不同數(shù)據(jù)類(lèi)型的內(nèi)存使用情況。首先,我們先來(lái)看看各個(gè)數(shù)據(jù)類(lèi)型的平均內(nèi)存用量。

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))

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

可以看出,78 個(gè) object 列所使用的內(nèi)存量最大。我們后面再具體談這個(gè)問(wèn)題。首先我們看看能否改進(jìn)數(shù)值列的內(nèi)存用量。

理解子類(lèi)型(subtype)

正如我們前面簡(jiǎn)單提到的那樣,pandas 內(nèi)部將數(shù)值表示為 NumPy ndarrays,并將它們存儲(chǔ)在內(nèi)存的連續(xù)塊中。這種存儲(chǔ)模式占用的空間更少,而且也讓我們可以快速訪問(wèn)這些值。因?yàn)?pandas 表示同一類(lèi)型的每個(gè)值時(shí)都使用同樣的字節(jié)數(shù),而 NumPy ndarray 可以存儲(chǔ)值的數(shù)量,所以 pandas 可以快速準(zhǔn)確地返回一個(gè)數(shù)值列所消耗的字節(jié)數(shù)。

pandas 中的許多類(lèi)型都有多個(gè)子類(lèi)型,這些子類(lèi)型可以使用更少的字節(jié)來(lái)表示每個(gè)值。比如說(shuō) float 類(lèi)型就包含 float16、float32 和 float64 子類(lèi)型。類(lèi)型名稱中的數(shù)字就代表該類(lèi)型表示值的位(bit)數(shù)。比如說(shuō),我們剛剛列出的子類(lèi)型就分別使用了 2、4、8、16 個(gè)字節(jié)。下面的表格給出了 pandas 中最常用類(lèi)型的子類(lèi)型:

19b949d8-fe7e-11eb-9bcf-12bb97331649.png

一個(gè) int8 類(lèi)型的值使用 1 個(gè)字節(jié)的存儲(chǔ)空間,可以表示 256(2^8)個(gè)二進(jìn)制數(shù)。這意味著我們可以使用這個(gè)子類(lèi)型來(lái)表示從 -128 到 127(包括 0)的所有整數(shù)值。

我們可以使用 numpy.iinfo 類(lèi)來(lái)驗(yàn)證每個(gè)整型數(shù)子類(lèi)型的最大值和最小值。舉個(gè)例子:

import numpy as np

int_types = [“uint8”, “int8”, “int16”]

for it in int_types:

print(np.iinfo(it))

Machine parameters for uint8

---------------------------------------------------------------

min = 0

max = 255

---------------------------------------------------------------

Machine parameters for int8

---------------------------------------------------------------

min = -128

max = 127

---------------------------------------------------------------

Machine parameters for int16

---------------------------------------------------------------

min = -32768

max = 32767

---------------------------------------------------------------

這里我們可以看到 uint(無(wú)符號(hào)整型)和 int(有符號(hào)整型)之間的差異。這兩種類(lèi)型都有一樣的存儲(chǔ)能力,但其中一個(gè)只保存 0 和正數(shù)。無(wú)符號(hào)整型讓我們可以更有效地處理只有正數(shù)值的列。

使用子類(lèi)型優(yōu)化數(shù)值列

我們可以使用函數(shù) pd.to_numeric() 來(lái)對(duì)我們的數(shù)值類(lèi)型進(jìn)行 downcast(向下轉(zhuǎn)型)操作。我們會(huì)使用 DataFrame.select_dtypes 來(lái)選擇整型列,然后我們會(huì)對(duì)其數(shù)據(jù)類(lèi)型進(jìn)行優(yōu)化,并比較內(nèi)存用量。

# We‘re going to be calculating memory usage a lot,

# so we’ll create a function to save us some time!

def mem_usage(pandas_obj):

if isinstance(pandas_obj,pd.DataFrame):

usage_b = pandas_obj.memory_usage(deep=True).sum()

else: # we assume if not a df it‘s a series

usage_b = pandas_obj.memory_usage(deep=True)

usage_mb = usage_b / 1024 ** 2 # convert bytes to megabytes

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)

7.87 MB

1.48 MB

19c5962a-fe7e-11eb-9bcf-12bb97331649.png

我們可以看到內(nèi)存用量從 7.9 MB 下降到了 1.5 MB,降低了 80% 以上。但這對(duì)我們?cè)?dataframe 的影響并不大,因?yàn)槠渲械恼土蟹浅I佟?/p>

讓我們對(duì)其中的浮點(diǎn)型列進(jìn)行一樣的操作。

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)

100.99 MB

50.49 MB

19da575e-fe7e-11eb-9bcf-12bb97331649.png

我們可以看到浮點(diǎn)型列的數(shù)據(jù)類(lèi)型從 float64 變成了 float32,讓內(nèi)存用量降低了 50%。

讓我們?yōu)樵?dataframe 創(chuàng)建一個(gè)副本,并用這些優(yōu)化后的列替換原來(lái)的列,然后看看我們現(xiàn)在的整體內(nèi)存用量。

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))

861.57 MB

804.69 MB

盡管我們極大地減少了數(shù)值列的內(nèi)存用量,但整體的內(nèi)存用量?jī)H減少了 7%。我們的大部分收獲都將來(lái)自對(duì) object 類(lèi)型的優(yōu)化。

在我們開(kāi)始行動(dòng)之前,先看看 pandas 中字符串的存儲(chǔ)方式與數(shù)值類(lèi)型的存儲(chǔ)方式的比較。

數(shù)值存儲(chǔ)與字符串存儲(chǔ)的比較

object 類(lèi)型表示使用 Python 字符串對(duì)象的值,部分原因是 NumPy 不支持缺失(missing)字符串類(lèi)型。因?yàn)?Python 是一種高級(jí)的解釋性語(yǔ)言,它對(duì)內(nèi)存中存儲(chǔ)的值沒(méi)有細(xì)粒度的控制能力。

這一限制導(dǎo)致字符串的存儲(chǔ)方式很碎片化,從而會(huì)消耗更多內(nèi)存,而且訪問(wèn)速度也更慢。object 列中的每個(gè)元素實(shí)際上都是一個(gè)指針,包含了實(shí)際值在內(nèi)存中的位置的「地址」。

下面這幅圖給出了以 NumPy 數(shù)據(jù)類(lèi)型存儲(chǔ)數(shù)值數(shù)據(jù)和使用 Python 內(nèi)置類(lèi)型存儲(chǔ)字符串?dāng)?shù)據(jù)的方式。

19e9da1c-fe7e-11eb-9bcf-12bb97331649.jpg

圖片來(lái)源:https://jakevdp.github.io/blog/2014/05/09/why-python-is-slow/

在前面的表格中,你可能已經(jīng)注意到 object 類(lèi)型的內(nèi)存使用是可變的。盡管每個(gè)指針僅占用 1 字節(jié)的內(nèi)存,但如果每個(gè)字符串在 Python 中都是單獨(dú)存儲(chǔ)的,那就會(huì)占用實(shí)際字符串那么大的空間。我們可以使用 sys.getsizeof() 函數(shù)來(lái)證明這一點(diǎn),首先查看單個(gè)的字符串,然后查看 pandas series 中的項(xiàng)。

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))

60

65

74

74

obj_series = pd.Series([’working out‘,

’memory usage for‘,

’strings in python is fun!‘,

’strings in python is fun!‘])

obj_series.apply(getsizeof)

0 60

1 65

2 74

3 74

dtype: int64

你可以看到,當(dāng)存儲(chǔ)在 pandas series 時(shí),字符串的大小與用 Python 單獨(dú)存儲(chǔ)的字符串的大小是一樣的。

使用 Categoricals 優(yōu)化 object 類(lèi)型

pandas 在 0.15 版引入了 Categorials。category 類(lèi)型在底層使用了整型值來(lái)表示一個(gè)列中的值,而不是使用原始值。pandas 使用一個(gè)單獨(dú)的映射詞典將這些整型值映射到原始值。只要當(dāng)一個(gè)列包含有限的值的集合時(shí),這種方法就很有用。當(dāng)我們將一列轉(zhuǎn)換成 category dtype 時(shí),pandas 就使用最節(jié)省空間的 int 子類(lèi)型來(lái)表示該列中的所有不同值。

19ff414a-fe7e-11eb-9bcf-12bb97331649.png

為了了解為什么我們可以使用這種類(lèi)型來(lái)減少內(nèi)存用量,讓我們看看我們的 object 類(lèi)型中每種類(lèi)型的不同值的數(shù)量。

gl_obj = gl.select_dtypes(include=[’object‘]).copy()

gl_obj.describe()

1a0b0354-fe7e-11eb-9bcf-12bb97331649.png

上圖完整圖像詳見(jiàn)原文

大概看看就能發(fā)現(xiàn),對(duì)于我們整個(gè)數(shù)據(jù)集的 172,000 場(chǎng)比賽,其中不同(unique)值的數(shù)量可以說(shuō)非常少。

為了了解當(dāng)我們將其轉(zhuǎn)換成 categorical 類(lèi)型時(shí)究竟發(fā)生了什么,我們拿出一個(gè) object 列來(lái)看看。我們將使用數(shù)據(jù)集的第二列 day_of_week.

看看上表,可以看到其僅包含 7 個(gè)不同的值。我們將使用 .astype() 方法將其轉(zhuǎn)換成 categorical 類(lèi)型。

dow = gl_obj.day_of_week

print(dow.head())

dow_cat = dow.astype(’category‘)

print(dow_cat.head())

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]

如你所見(jiàn),除了這一列的類(lèi)型發(fā)生了改變之外,數(shù)據(jù)看起來(lái)還是完全一樣。讓我們看看這背后發(fā)生了什么。

在下面的代碼中,我們使用了 Series.cat.codes 屬性來(lái)返回 category 類(lèi)型用來(lái)表示每個(gè)值的整型值。

dow_cat.head().cat.codes

0 4

1 0

2 2

3 1

4 5

dtype: int8

你可以看到每個(gè)不同值都被分配了一個(gè)整型值,而該列現(xiàn)在的基本數(shù)據(jù)類(lèi)型是 int8。這一列沒(méi)有任何缺失值,但就算有,category 子類(lèi)型也能處理,只需將其設(shè)置為 -1 即可。

最后,讓我們看看在將這一列轉(zhuǎn)換為 category 類(lèi)型前后的內(nèi)存用量對(duì)比。

print(mem_usage(dow))

print(mem_usage(dow_cat))

9.84 MB

0.16 MB

9.8 MB 的內(nèi)存用量減少到了 0.16 MB,減少了 98%!注意,這個(gè)特定列可能代表了我們最好的情況之一——即大約 172,000 項(xiàng)卻只有 7 個(gè)不同的值。

盡管將所有列都轉(zhuǎn)換成這種類(lèi)型聽(tīng)起來(lái)很吸引人,但了解其中的取舍也很重要。最大的壞處是無(wú)法執(zhí)行數(shù)值計(jì)算。如果沒(méi)有首先將其轉(zhuǎn)換成數(shù)值 dtype,那么我們就無(wú)法對(duì) category 列進(jìn)行算術(shù)運(yùn)算,也就是說(shuō)無(wú)法使用 Series.min() 和 Series.max() 等方法。

我們應(yīng)該堅(jiān)持主要將 category 類(lèi)型用于不同值的數(shù)量少于值的總數(shù)量的 50% 的 object 列。如果一列中的所有值都是不同的,那么 category 類(lèi)型所使用的內(nèi)存將會(huì)更多。因?yàn)檫@一列不僅要存儲(chǔ)所有的原始字符串值,還要額外存儲(chǔ)它們的整型值代碼。你可以在 pandas 文檔中了解 category 類(lèi)型的局限性:http://pandas.pydata.org/pandas-docs/stable/categorical.html。

我們將編寫(xiě)一個(gè)循環(huán)函數(shù)來(lái)迭代式地檢查每一 object 列中不同值的數(shù)量是否少于 50%;如果是,就將其轉(zhuǎn)換成 category 類(lèi)型。

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]

和之前一樣進(jìn)行比較:

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)

752.72 MB

51.67 MB

1a1611ea-fe7e-11eb-9bcf-12bb97331649.png

在這個(gè)案例中,所有的 object 列都被轉(zhuǎn)換成了 category 類(lèi)型,但并非所有數(shù)據(jù)集都是如此,所以你應(yīng)該使用上面的流程進(jìn)行檢查。

object 列的內(nèi)存用量從 752MB 減少到了 52MB,減少了 93%。讓我們將其與我們 dataframe 的其它部分結(jié)合起來(lái),看看從最初 861MB 的基礎(chǔ)上實(shí)現(xiàn)了多少進(jìn)步。

optimized_gl[converted_obj.columns] = converted_obj

mem_usage(optimized_gl)

’103.64 MB‘

Wow,進(jìn)展真是不錯(cuò)!我們還可以執(zhí)行另一項(xiàng)優(yōu)化——如果你記得前面給出的數(shù)據(jù)類(lèi)型表,你知道還有一個(gè) datetime 類(lèi)型。這個(gè)數(shù)據(jù)集的第一列就可以使用這個(gè)類(lèi)型。

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

你可能記得這一列開(kāi)始是一個(gè)整型,現(xiàn)在已經(jīng)優(yōu)化成了 unint32 類(lèi)型。因此,將其轉(zhuǎn)換成 datetime 類(lèi)型實(shí)際上會(huì)讓內(nèi)存用量翻倍,因?yàn)?datetime 類(lèi)型是 64 位的。將其轉(zhuǎn)換成 datetime 類(lèi)型是有價(jià)值的,因?yàn)檫@讓我們可以更好地進(jìn)行時(shí)間序列分析。

pandas.to_datetime() 函數(shù)可以幫我們完成這種轉(zhuǎn)換,使用其 format 參數(shù)將我們的日期數(shù)據(jù)存儲(chǔ)成 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]

在讀入數(shù)據(jù)的同時(shí)選擇類(lèi)型

現(xiàn)在,我們已經(jīng)探索了減少現(xiàn)有 dataframe 的內(nèi)存占用的方法。通過(guò)首先讀入 dataframe,然后在這個(gè)過(guò)程中迭代以減少內(nèi)存占用,我們了解了每種優(yōu)化方法可以帶來(lái)的內(nèi)存減省量。但是正如我們前面提到的一樣,我們往往沒(méi)有足夠的內(nèi)存來(lái)表示數(shù)據(jù)集中的所有值。如果我們一開(kāi)始甚至無(wú)法創(chuàng)建 dataframe,我們又可以怎樣應(yīng)用節(jié)省內(nèi)存的技術(shù)呢?

幸運(yùn)的是,我們可以在讀入數(shù)據(jù)的同時(shí)指定最優(yōu)的列類(lèi)型。pandas.read_csv() 函數(shù)有幾個(gè)不同的參數(shù)讓我們可以做到這一點(diǎn)。dtype 參數(shù)接受具有(字符串)列名稱作為鍵值(key)以及 NumPy 類(lèi)型 object 作為值的詞典。

首先,我們可將每一列的最終類(lèi)型存儲(chǔ)在一個(gè)詞典中,其中鍵值表示列名稱,首先移除日期列,因?yàn)槿掌诹行枰煌奶幚矸绞健?/p>

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))

# rather than print all 161 items, we’ll

# sample 10 key/value pairs from the dict

# and print it nicely using prettyprint

preview = first2pairs = {key:value for key,value in list(column_types.items())[:10]}

import pprint

pp = pp = pprint.PrettyPrinter(indent=4)

pp.pprint(preview)

{ ‘a(chǎn)cquisition_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’}

現(xiàn)在我們可以使用這個(gè)詞典了,另外還有幾個(gè)參數(shù)可用于按正確的類(lèi)型讀入日期,而且僅需幾行代碼:

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

1a21aba4-fe7e-11eb-9bcf-12bb97331649.png

上圖完整圖像詳見(jiàn)原文

通過(guò)優(yōu)化這些列,我們成功將 pandas 的內(nèi)存占用從 861.6MB 減少到了 104.28MB——減少了驚人的 88%!

分析棒球比賽

現(xiàn)在我們已經(jīng)優(yōu)化好了我們的數(shù)據(jù),我們可以執(zhí)行一些分析了。讓我們先從了解這些比賽的日期分布開(kāi)始。

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=‘a(chǎn)rea’,stacked=‘true’)

ax.legend(loc=‘upper right’)

ax.set_ylim(0,1)

plt.show()

我們可以看到在 1920 年代以前,星期日的棒球比賽很少,但在上個(gè)世紀(jì)后半葉就變得越來(lái)越多了。

我們也可以清楚地看到過(guò)去 50 年來(lái),比賽的日期分布基本上沒(méi)什么大變化了。

讓我們?cè)倏纯幢荣悤r(shí)長(zhǎng)的變化情況:

game_lengths = optimized_gl.pivot_table(index=‘year’, values=‘length_minutes’)

game_lengths.reset_index().plot.scatter(‘year’,‘length_minutes’)

plt.show()

從 1940 年代以來(lái),棒球比賽的持續(xù)時(shí)間越來(lái)越長(zhǎng)。

總結(jié)和下一步

我們已經(jīng)了解了 pandas 使用不同數(shù)據(jù)類(lèi)型的方法,然后我們使用這種知識(shí)將一個(gè) pandas dataframe 的內(nèi)存用量減少了近 90%,而且也僅使用了一些簡(jiǎn)單的技術(shù):

將數(shù)值列向下轉(zhuǎn)換成更高效的類(lèi)型

將字符串列轉(zhuǎn)換成 categorical 類(lèi)型

責(zé)任編輯:haq

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 數(shù)據(jù)
    +關(guān)注

    關(guān)注

    8

    文章

    6754

    瀏覽量

    88612
  • 內(nèi)存
    +關(guān)注

    關(guān)注

    8

    文章

    2942

    瀏覽量

    73727

原文標(biāo)題:簡(jiǎn)單實(shí)用的pandas技巧:如何將內(nèi)存占用降低90%

文章出處:【微信號(hào):aming_linux,微信公眾號(hào):阿銘linux】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    labview數(shù)據(jù)類(lèi)型的取值范圍是多少

    LabVIEW的數(shù)據(jù)類(lèi)型豐富多樣,涵蓋了整數(shù)、小數(shù)(浮點(diǎn)數(shù))、復(fù)數(shù)等多種類(lèi)型,每種類(lèi)型都有其特定的取值范圍。以下是對(duì)LabVIEW中常見(jiàn)數(shù)據(jù)類(lèi)型取值范圍的說(shuō)明: 整數(shù)
    的頭像 發(fā)表于 09-04 17:33 ?485次閱讀

    技術(shù)干貨驛站 ▏深入理解C語(yǔ)言:基本數(shù)據(jù)類(lèi)型和變量

    在C語(yǔ)言中,數(shù)據(jù)類(lèi)型和變量是編程的基礎(chǔ),也是理解更復(fù)雜概念的關(guān)鍵。數(shù)據(jù)類(lèi)型決定了變量的內(nèi)存分配、存儲(chǔ)范圍和操作方式,而變量則是存儲(chǔ)數(shù)據(jù)的容器。本篇文章將從基本
    的頭像 發(fā)表于 07-26 17:53 ?1764次閱讀
    技術(shù)干貨驛站 ▏深入理解C語(yǔ)言:基本<b class='flag-5'>數(shù)據(jù)類(lèi)型</b>和變量

    C語(yǔ)言數(shù)據(jù)類(lèi)型有哪些

    在 C 語(yǔ)言中,數(shù)據(jù)類(lèi)型指的是用于聲明不同類(lèi)型的變量或函數(shù)的一個(gè)廣泛的系統(tǒng)。變量的類(lèi)型決定了變量存儲(chǔ)占用的空間,以及如何解釋存儲(chǔ)的位模式。
    發(fā)表于 03-20 10:56 ?376次閱讀
    C語(yǔ)言<b class='flag-5'>數(shù)據(jù)類(lèi)型</b>有哪些

    plc數(shù)據(jù)類(lèi)型怎么理解和應(yīng)用

    PLC(可編程邏輯控制器)是一種工業(yè)自動(dòng)化設(shè)備,用于控制機(jī)械和工業(yè)過(guò)程。在PLC編程中,數(shù)據(jù)類(lèi)型是非常重要的概念,因?yàn)樗鼪Q定了程序中數(shù)據(jù)的存儲(chǔ)和處理方式。正確理解和應(yīng)用PLC數(shù)據(jù)類(lèi)型是編寫(xiě)有效、可靠
    的頭像 發(fā)表于 12-19 11:39 ?3686次閱讀

    oracle的數(shù)據(jù)類(lèi)型有哪些

    Oracle數(shù)據(jù)庫(kù)中有許多數(shù)據(jù)類(lèi)型可供選擇,每種數(shù)據(jù)類(lèi)型都有其各自的特點(diǎn)和適用場(chǎng)景。下面是對(duì)Oracle數(shù)據(jù)庫(kù)中最常用的
    的頭像 發(fā)表于 12-05 16:45 ?2138次閱讀

    byte屬于java基本類(lèi)型

    位帶符號(hào)的二進(jìn)制數(shù),取值范圍為-128到127。 在Java中,基本數(shù)據(jù)類(lèi)型與引用數(shù)據(jù)類(lèi)型不同,基本數(shù)據(jù)類(lèi)型是存儲(chǔ)在棧內(nèi)存中的,而引用數(shù)據(jù)類(lèi)型
    的頭像 發(fā)表于 12-05 10:40 ?745次閱讀

    php的數(shù)據(jù)類(lèi)型主要有哪幾種

    PHP是一種強(qiáng)類(lèi)型編程語(yǔ)言,它支持多種數(shù)據(jù)類(lèi)型。以下是PHP的主要數(shù)據(jù)類(lèi)型: 字符串(String): 表示文本數(shù)據(jù),可以使用單引號(hào)或雙引號(hào)來(lái)定義字符串。例如:$str = "Hell
    的頭像 發(fā)表于 12-04 16:05 ?622次閱讀

    javascript的typeof返回哪些數(shù)據(jù)類(lèi)型?

    JavaScript的typeof操作符用于確定一個(gè)值的數(shù)據(jù)類(lèi)型,可能的返回值包括以下幾種: "undefined":當(dāng)一個(gè)變量被聲明但未被賦值時(shí),其類(lèi)型為undefined。 "boolean
    的頭像 發(fā)表于 12-03 11:41 ?733次閱讀

    javascript的基本數(shù)據(jù)類(lèi)型有哪些

    JavaScript 是一種動(dòng)態(tài)的、面向?qū)ο蟮木幊陶Z(yǔ)言,廣泛應(yīng)用于 Web 開(kāi)發(fā)中。在 JavaScript 中,有七種基本數(shù)據(jù)類(lèi)型(Primitive Types),它們分別是 Undefined
    的頭像 發(fā)表于 12-03 11:17 ?673次閱讀

    使用pandas進(jìn)行數(shù)據(jù)選擇和過(guò)濾的基本技術(shù)和函數(shù)

    Python pandas庫(kù)提供了幾種選擇和過(guò)濾數(shù)據(jù)的方法,如loc、iloc、[]括號(hào)操作符、query、isin、between等等
    的頭像 發(fā)表于 12-01 10:14 ?310次閱讀
    使用<b class='flag-5'>pandas</b>進(jìn)行<b class='flag-5'>數(shù)據(jù)</b><b class='flag-5'>選擇</b>和過(guò)濾的基本技術(shù)和函數(shù)

    redis的五種數(shù)據(jù)類(lèi)型

    Redis是一種高性能的內(nèi)存數(shù)據(jù)庫(kù),常用于緩存、任務(wù)隊(duì)列、分布式鎖等場(chǎng)景。它提供了多種數(shù)據(jù)類(lèi)型來(lái)滿足各種不同的需求,包括字符串(string)、哈希(hash)、列表(list)、集合(set
    的頭像 發(fā)表于 11-16 11:06 ?561次閱讀

    如何將ROS數(shù)據(jù)類(lèi)型轉(zhuǎn)換為MATLAB數(shù)據(jù)類(lèi)型

    。 例如,四元數(shù)消息包含w、x、y和z屬性,但該消息并不強(qiáng)制四元數(shù)作為一個(gè)整體是有效的。所以有可能在單獨(dú)修改一個(gè)四元數(shù)信息后,這個(gè)四元數(shù)是不符合模型的。 同時(shí)消息屬性還可以具有各種數(shù)據(jù)類(lèi)型。MATLAB使用ROS設(shè)置的規(guī)則來(lái)確定這些數(shù)據(jù)類(lèi)型。 為此,這些
    的頭像 發(fā)表于 11-15 15:24 ?496次閱讀
    如何將ROS<b class='flag-5'>數(shù)據(jù)類(lèi)型</b>轉(zhuǎn)換為MATLAB<b class='flag-5'>數(shù)據(jù)類(lèi)型</b>

    數(shù)據(jù)的位是什么?C語(yǔ)言中常見(jiàn)的數(shù)據(jù)類(lèi)型有哪些?

    本文介紹關(guān)于C語(yǔ)言中數(shù)據(jù)類(lèi)型的相關(guān)知識(shí),比如常見(jiàn)的數(shù)據(jù)類(lèi)型有哪些,怎么定義和使用數(shù)據(jù)類(lèi)型等。 1 、數(shù)據(jù)的位是什么? 計(jì)算機(jī)系統(tǒng)的存儲(chǔ)最小單元為一個(gè)二進(jìn)制位,每一位要么0要么1。一般來(lái)
    的頭像 發(fā)表于 11-08 15:55 ?1288次閱讀
    <b class='flag-5'>數(shù)據(jù)</b>的位是什么?C語(yǔ)言中常見(jiàn)的<b class='flag-5'>數(shù)據(jù)類(lèi)型</b>有哪些?

    SystemC中的數(shù)據(jù)類(lèi)型概念

    引起一個(gè)事件,要使用通知函數(shù)notify( ),語(yǔ)法如下: event_name. notify (參數(shù)); 或 notify (參數(shù),event_name); SystemC中的數(shù)據(jù)類(lèi)型 作為C++
    的頭像 發(fā)表于 11-02 15:44 ?807次閱讀
    SystemC中的<b class='flag-5'>數(shù)據(jù)類(lèi)型</b>概念

    C語(yǔ)言數(shù)據(jù)類(lèi)型詳解

    計(jì)算機(jī)編程語(yǔ)言是用來(lái)控制計(jì)算機(jī)的行為及操作,協(xié)助人們解決現(xiàn)實(shí)中的問(wèn)題,其能表達(dá)的數(shù)據(jù)類(lèi)型也是從實(shí)際中提取并抽象出來(lái)形成的數(shù)據(jù)結(jié)構(gòu)描述。
    發(fā)表于 11-02 11:32 ?1147次閱讀
    C語(yǔ)言<b class='flag-5'>數(shù)據(jù)類(lèi)型</b>詳解