1、读取大文件遇到的困难
①用Office软件打不开
②用pd.read_csv读取超级慢,占用很多内存
2、大数据csv读入到内存
①快速读取多个分块对象(没有将实际数据读入,提数据时才读),得到一个可迭代对象TextFileReader:data = pd.read_csv(‘train.csv’,chunksize=块大小),数据处理和清洗都用分块的方式处理,大大降低内存使用1
2
3
4
5
6
7t=[]
for chunk_data in data: # 提取每一块数据
print(len(chunk_data.index)) #每一块的行数,都是5
for i in range(len(chunk_data.index)):
row_data = chunk_data.iloc[i] #是Series,索引是原数据的列标签
list_data = list(row_data) #得到列表
t.append(list_data) #嵌套列表
②读取需要的列:c = [‘列1’,’列2’]; data = pd.read_csv(‘train.csv’,usecols = c, chunksize=块大小)
③读取前5行数据:data = pd.read_csv(‘train.csv’,iterator=True),返回一个可迭代对象TextFileReader,再用get_chunk(5)方法返回前5行数据
3、将数据写入硬盘
①对于第一个分块:保留header信息,’w’模式写入 data.to_csv(‘result.csv’,index = False, mode = ‘w’)
②对于其它分块:去除header信息,’a’模式写入(不删除原文档,接着原文档后继续写) data.to_csv(‘result.csv’,index = False, header = False, mode = ‘a’)
③如果数据量较少,用pickle读取和写入1
2
3
4
5
6# 读取
def read_trainingSet(fileLoc):
import cPickle as pickle
with open(fileLoc, 'r') as f:
pack = pickle.load(f)
return pack[0], pack[1]
1 | # 写入 |
4、数据集的行、列合并
list的列合并效果不好:list需要拆解每一行元素,并用extend拓展每一行的列元素;行合并可用append方法逐行读取
DataFrame用concat方法合并:pd.concat([a,b],axis=1) axis=0表示行合并,跑起来会很慢(因为concat静态性,每次需要重新分配资源)
5、pandas降低内存占用
用pd.concat合并数据框发生内存溢出的情况,思考如何降低内存的使用量。1
2
3#读取100万条数据
data = pd.read_csv('train.csv',iterator=True)
train = data.get_chunk(1000000)
1 | >> train.info(memory_usage='deep') |
可以看到该分块有100万行24列,pandas会自动为我们检测数据类型,发现其中有9列是object(是指有字符串或包含混合数据类型的情况)
(1)pandas如何将数据存储在内存中
在pandas内部,不同数据类型都是分开存储的,相同数据类型的列会组织成同一个值块(比如IntBlock、ObjectBlock、FloatBlock),这些块没有保留原来列的名称(BlockManager类负责保留了行列索引与实际块之间的映射关系)
看看各个数据类型平均内存使用量:1
2
3
4
5
6
7
8
9>> for dtype in ['float64','int64','object']:
s = train.select_dtypes(include=[dtype])
mb = s.memory_usage(deep=True).mean()
b = mb/1024**2
print("{}类型占用的平均内存是{:03.2f}MB".format(dtype,b))
float64类型占用的平均内存是3.81MB
int64类型占用的平均内存是7.12MB
object类型占用的平均内存是55.79MB
发现object类使用的内存量最大。
(2)int类型的内存使用
pandas内部将数值表示为numpy ndarrays,并把它们存储在内存的连续块中,这样存储模式占用的空间更少,也能让我们快速访问这些值。pandas的同一类型的每个值都用相同的字节数,而numpy ndarrays可以存储值的数量,所以pandas可以快速反馈一个数值列消耗的字节数。
pandas的很多类型都有子类型,这些子类型可以用更少的字节来表示每个值,比如int8、int16、int32、int64子类型,名称中的数字就代表该类型表示值的位数,比如上面3个类型就分别使用了1、2、4、8个字节,int8类型的值可以表示-128到127之间的所有整数。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34#用numpy.info来检验每个整数子类型的最大值、最小值
>> int_types = ['uint8','int8','int16','int32','int64']
>> 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
------------------------------------------------------
Machine parameters for int32
------------------------------------------------------
min = -2147483648
max = 2147483647
------------------------------------------------------
Machine parameters for int64
------------------------------------------------------
min = -9223372036854775808
max = 9223372036854775807
------------------------------------------------------
(3)用子类型优化int数值列
用df.select_dtypes来选择整数列,用pd.to_numeric()来对我们的数值类型进行downcast(向下转型)操作,对数据类型进行优化,并比较内存用量。1
2
3
4
5
6
7
8
9
10
11
12
13
14def mem_usage(p):
if isinstance(p, pd.DataFrame):
b = p.memory_usage(deep=True).sum()
else:
b = p.memory_usage(deep=True)
mb = b/1024**2
return "{:03.2f}MB".format(mb)
chunkint = train.select_dtypes(include=['int64'])
convertint = chunkint.apply(pd.to_numeric, downcast='unsigned')
print(mem_usage(chunkint))
print(mem_usage(convertint))
# 返回106.81MB 28.61MB
1 | compare_ints=pd.concat([chunkint.dtypes,convertint.dtypes],axis=1) |
发现内存用量从106.81MB降到了8.61MB,很多unint64类型的数值数据,降到了uint8、unint16、unint32类型
(4)用子类型优化float浮点型列
1 | chunkfloat = train.select_dtypes(include=['float64']) |
浮点数从float64降到了float32,让内存用量降低一半
(5)查看整体内存用量变化
为原始数据框创建一个副本,用优化后的列替换原来的列,看看整体内存用量。1
2
3
4
5
6optdf = train.copy()
optdf[convertint.columns] = convertint
optdf[convertfloat.columns] = convertfloat
print(mem_usage(train))
print(mem_usage(optdf))
#返回672.34MB 590.32MB
尽管我们极大地减少了数值列的内存用量,但整体的内存用量仅减少了12%,我们大部分的收获都将来自对object类型的优化。
(6)pandas中字符串的存储方式
object类型表示用字符串对象,一部分原因是numpy不支持缺失字符串类型。因为python是一种高级的解释型语言,对内存中存储的值没有细粒度的控制能力,这导致字符串的存储方式碎片化,会消耗很多内存,访问速度也很慢。
object列中每个元素实际上都是一个指针,包含了实际值在内存中的位置。尽管每个指针只占用1字节的内存,但如果每个字符串都单独存储,也会占用很大的空间。证明:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#查看单个字符串
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
#将这4个字符串存储在pd.series中,查看字符串大小
objseries = pd.Series([s1,s2,s3,s4])
objseries.apply(getsizeof)
#返回
0 60
1 65
2 74
3 74
dtype: int64
#说明存在series的字符串大小,与单独存储的字符串大小一样
(7)用Categoricals优化object类型
pandas在0.15版引入了Categoricals,Category类型在底层用int子类型表示字符串。pandas使用一个单独的映射字典,将数值映射到原始字符串,只要当一个列中包含有限的值,这种方法就有用。来看看object类型中每种类型的不同值数量。1
2objdata = train.select_dtypes(include=['object']).copy()
objdata.describe()

拿出一个object列(site_category)来看看,将它转换成categorical类型时发生了什么。1
2
3
4s = train.site_category
sc = s.astype('category')
print(s.head())
print(sc.head())

所以除了这一列的类型发生改变外,数据看起来完全一样,再来看看这背后发生了什么:1
2
3
4
5
6
7
8sc.head().cat.codes #返回category类型用来表示每个值的整型值
#返回
0 2
1 2
2 2
3 2
4 0
dtype: int8
可看到每个不同值都被分配了一个整型值,该列现在的基本数据类型是int8(如果有缺失值,会被设置为-1)。再来看看转换前后的内存使用量对比:1
2
3print(mem_usage(s))
print(mem_usage(sc))
#返回61.99MB 0.96MB
注意,category类型不能进行算术运算,也不适用于不同值数量 > 总数量50%的列(内存会占用更多),现在编写一个循环函数来迭代检查每一个object列中不同值的数量是否少于50%,如果是就将其转换成category类型。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20conver = pd.DataFrame()
for col in objdata.columns:
num_unique = len(objdata[col].unique())
num_total = len(objdata[col])
if num_unique/num_total < 0.5:
conver.loc[:,col] = objdata[col].astype('category')
else:
conver.loc[:,col] = objdata[col]
print(mem_usage(objdata))
print(mem_usage(conver))
#返回557.90MB 57.24MB
compare_obj=pd.concat([objdata.dtypes,conver.dtypes],axis=1)
compare_obj.columns = ['before','after']
compare_obj.apply(pd.Series.value_counts)
#返回
before after
object 78.0 NaN
category NaN 78.0
1 | #与数值类型的列结合 |
(8)读入数据时指定最优的数据类型
pd.read_csv()指定参数dtype,它接受一个字典(key是列名称,value是类型)1
2
3
4
5
6
7
8
9#将每一列的最优类型存储在dict中
dtypes = optdf.dtypes
col = dtypes.index
opttype = [i.name for i in dtypes.values]
col_dict = dict(zip(col,opttype))
train1 = train.astype(col_dict)
mem_usage(train1)
#返回89.66MB