替换类别数据 (pandas)
Replacing Category Data (pandas)
我有一些包含多个类别列的大文件。类别也是一个很大的词,因为这些基本上是 descriptions/partial 个句子。
以下是每个类别的唯一值:
Category 1 = 15
Category 2 = 94
Category 3 = 294
Category 4 = 401
Location 1 = 30
Location 2 = 60
然后甚至还有具有重复数据(名字、姓氏、ID 等)的用户。
我正在考虑以下解决方案来缩小文件大小:
1) 创建一个文件,用唯一的整数匹配每个类别
2) 创建一个地图(有没有办法通过读取另一个文件来做到这一点?就像我创建一个 .csv 并将其作为另一个数据帧加载然后匹配它?或者我真的必须输入它最初?)
或
3) 基本上做一个连接(VLOOKUP),然后用长对象名称删除旧列
pd.merge(df1, categories, on = 'Category1', how = 'left')
del df1['Category1']
在这种情况下,人们通常会做什么?这些文件非常大。 60 列,大部分数据都是长的、重复的类别和时间戳。从字面上看,根本没有数字数据。这对我来说很好,但由于共享驱动器 space 分配超过几个月,共享文件几乎是不可能的。
Pandas 有一个 Categorical
数据类型可以做到这一点。它基本上将类别映射到幕后的整数。
Internally, the data structure consists of a categories array and an
integer array of codes which point to the real value in the categories
array.
文档是 here。
要在保存到 csv 时受益于 Categorical
dtype,您可能需要遵循以下过程:
- 将您的类别定义提取到单独的数据帧/文件中
- 将您的分类数据转换为 int 代码
- 将转换后的 DataFrame 与定义数据帧一起保存到 csv
当您需要再次使用它们时:
- 从 csv 文件恢复数据帧
- 将具有 int 代码的数据框映射到类别定义
- 将映射列转换为分类
为了说明这个过程:
制作示例数据框:
df = pd.DataFrame(index=pd.np.arange(0,100000))
df.index.name = 'index'
df['categories'] = 'Category'
df['locations'] = 'Location'
n1 = pd.np.tile(pd.np.arange(1,5), df.shape[0]/4)
n2 = pd.np.tile(pd.np.arange(1,3), df.shape[0]/2)
df['categories'] = df['categories'] + pd.Series(n1).astype(str)
df['locations'] = df['locations'] + pd.Series(n2).astype(str)
print df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 100000 entries, 0 to 99999
Data columns (total 2 columns):
categories 100000 non-null object
locations 100000 non-null object
dtypes: object(2)
memory usage: 2.3+ MB
None
注意大小:2.3+ MB
- 这大约是您的 csv 文件的大小。
现在将这些数据转换为 Categorical
:
df['categories'] = df['categories'].astype('category')
df['locations'] = df['locations'].astype('category')
print df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 100000 entries, 0 to 99999
Data columns (total 2 columns):
categories 100000 non-null category
locations 100000 non-null category
dtypes: category(2)
memory usage: 976.6 KB
None
注意内存使用量下降到 976.6 KB
但是,如果您现在将其保存为 csv:
df.to_csv('test1.csv')
...你会在文件中看到这个:
index,categories,locations
0,Category1,Location1
1,Category2,Location2
2,Category3,Location1
3,Category4,Location2
这意味着 'Categorical' 已转换为字符串以保存在 csv 中。
因此,让我们在保存定义后删除 Categorical
数据中的标签:
categories_details = pd.DataFrame(df.categories.drop_duplicates(), columns=['categories'])
print categories_details
categories
index
0 Category1
1 Category2
2 Category3
3 Category4
locations_details = pd.DataFrame(df.locations.drop_duplicates(), columns=['locations'])
print locations_details
index
0 Location1
1 Location2
现在将 Categorical
转换为 int
dtype:
for col in df.select_dtypes(include=['category']).columns:
df[col] = df[col].cat.codes
print df.head()
categories locations
index
0 0 0
1 1 1
2 2 0
3 3 1
4 0 0
print df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 100000 entries, 0 to 99999
Data columns (total 2 columns):
categories 100000 non-null int8
locations 100000 non-null int8
dtypes: int8(2)
memory usage: 976.6 KB
None
将转换后的数据保存到 csv
并注意该文件现在只有没有标签的数字。
文件大小也将反映此更改。
df.to_csv('test2.csv')
index,categories,locations
0,0,0
1,1,1
2,2,0
3,3,1
同时保存定义:
categories_details.to_csv('categories_details.csv')
locations_details.to_csv('locations_details.csv')
当您需要恢复文件时,从 csv
个文件加载它们:
df2 = pd.read_csv('test2.csv', index_col='index')
print df2.head()
categories locations
index
0 0 0
1 1 1
2 2 0
3 3 1
4 0 0
print df2.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 100000 entries, 0 to 99999
Data columns (total 2 columns):
categories 100000 non-null int64
locations 100000 non-null int64
dtypes: int64(2)
memory usage: 2.3 MB
None
categories_details2 = pd.read_csv('categories_details.csv', index_col='index')
print categories_details2.head()
categories
index
0 Category1
1 Category2
2 Category3
3 Category4
print categories_details2.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 4 entries, 0 to 3
Data columns (total 1 columns):
categories 4 non-null object
dtypes: object(1)
memory usage: 64.0+ bytes
None
locations_details2 = pd.read_csv('locations_details.csv', index_col='index')
print locations_details2.head()
locations
index
0 Location1
1 Location2
print locations_details2.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 2 entries, 0 to 1
Data columns (total 1 columns):
locations 2 non-null object
dtypes: object(1)
memory usage: 32.0+ bytes
None
现在使用map
将int
编码数据替换为类别描述并将其转换为Categorical
:
df2['categories'] = df2.categories.map(categories_details2.to_dict()['categories']).astype('category')
df2['locations'] = df2.locations.map(locations_details2.to_dict()['locations']).astype('category')
print df2.head()
categories locations
index
0 Category1 Location1
1 Category2 Location2
2 Category3 Location1
3 Category4 Location2
4 Category1 Location1
print df2.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 100000 entries, 0 to 99999
Data columns (total 2 columns):
categories 100000 non-null category
locations 100000 non-null category
dtypes: category(2)
memory usage: 976.6 KB
None
注意内存使用回到我们第一次将数据转换为 Categorical
时的状态。
如果你需要多次重复这个过程,自动化这个过程应该不难。
这是一种在单个 .csv 中保存包含分类列的数据框的方法:
Example:
------ -------
Fatcol Thincol: unique strings once, then numbers
------ -------
"Alberta" "Alberta"
"BC" "BC"
"BC" 2 -- string 2
"Alberta" 1 -- string 1
"BC" 2
...
The "Thincol" on the right can be saved as is in a .csv file,
and expanded to the "Fatcol" on the left after reading it in;
this can halve the size of big .csv s with repeated strings.
Functions
---------
fatcol( col: Thincol ) -> Fatcol, list[ unique str ]
thincol( col: Fatcol ) -> Thincol, dict( unique str -> int ), list[ unique str ]
Here "Fatcol" and "Thincol" are type names for iterators, e.g. lists:
Fatcol: list of strings
Thincol: list of strings or ints or NaN s
If a `col` is a `pandas.Series`, its `.values` are used.
这将 700M .csv 压缩为 248M -- 但是 write_csv
在我的 imac 上以 ~ 1 MB/sec 运行。
我有一些包含多个类别列的大文件。类别也是一个很大的词,因为这些基本上是 descriptions/partial 个句子。
以下是每个类别的唯一值:
Category 1 = 15
Category 2 = 94
Category 3 = 294
Category 4 = 401
Location 1 = 30
Location 2 = 60
然后甚至还有具有重复数据(名字、姓氏、ID 等)的用户。
我正在考虑以下解决方案来缩小文件大小:
1) 创建一个文件,用唯一的整数匹配每个类别
2) 创建一个地图(有没有办法通过读取另一个文件来做到这一点?就像我创建一个 .csv 并将其作为另一个数据帧加载然后匹配它?或者我真的必须输入它最初?)
或
3) 基本上做一个连接(VLOOKUP),然后用长对象名称删除旧列
pd.merge(df1, categories, on = 'Category1', how = 'left')
del df1['Category1']
在这种情况下,人们通常会做什么?这些文件非常大。 60 列,大部分数据都是长的、重复的类别和时间戳。从字面上看,根本没有数字数据。这对我来说很好,但由于共享驱动器 space 分配超过几个月,共享文件几乎是不可能的。
Pandas 有一个 Categorical
数据类型可以做到这一点。它基本上将类别映射到幕后的整数。
Internally, the data structure consists of a categories array and an integer array of codes which point to the real value in the categories array.
文档是 here。
要在保存到 csv 时受益于 Categorical
dtype,您可能需要遵循以下过程:
- 将您的类别定义提取到单独的数据帧/文件中
- 将您的分类数据转换为 int 代码
- 将转换后的 DataFrame 与定义数据帧一起保存到 csv
当您需要再次使用它们时:
- 从 csv 文件恢复数据帧
- 将具有 int 代码的数据框映射到类别定义
- 将映射列转换为分类
为了说明这个过程:
制作示例数据框:
df = pd.DataFrame(index=pd.np.arange(0,100000))
df.index.name = 'index'
df['categories'] = 'Category'
df['locations'] = 'Location'
n1 = pd.np.tile(pd.np.arange(1,5), df.shape[0]/4)
n2 = pd.np.tile(pd.np.arange(1,3), df.shape[0]/2)
df['categories'] = df['categories'] + pd.Series(n1).astype(str)
df['locations'] = df['locations'] + pd.Series(n2).astype(str)
print df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 100000 entries, 0 to 99999
Data columns (total 2 columns):
categories 100000 non-null object
locations 100000 non-null object
dtypes: object(2)
memory usage: 2.3+ MB
None
注意大小:2.3+ MB
- 这大约是您的 csv 文件的大小。
现在将这些数据转换为 Categorical
:
df['categories'] = df['categories'].astype('category')
df['locations'] = df['locations'].astype('category')
print df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 100000 entries, 0 to 99999
Data columns (total 2 columns):
categories 100000 non-null category
locations 100000 non-null category
dtypes: category(2)
memory usage: 976.6 KB
None
注意内存使用量下降到 976.6 KB
但是,如果您现在将其保存为 csv:
df.to_csv('test1.csv')
...你会在文件中看到这个:
index,categories,locations
0,Category1,Location1
1,Category2,Location2
2,Category3,Location1
3,Category4,Location2
这意味着 'Categorical' 已转换为字符串以保存在 csv 中。
因此,让我们在保存定义后删除 Categorical
数据中的标签:
categories_details = pd.DataFrame(df.categories.drop_duplicates(), columns=['categories'])
print categories_details
categories
index
0 Category1
1 Category2
2 Category3
3 Category4
locations_details = pd.DataFrame(df.locations.drop_duplicates(), columns=['locations'])
print locations_details
index
0 Location1
1 Location2
现在将 Categorical
转换为 int
dtype:
for col in df.select_dtypes(include=['category']).columns:
df[col] = df[col].cat.codes
print df.head()
categories locations
index
0 0 0
1 1 1
2 2 0
3 3 1
4 0 0
print df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 100000 entries, 0 to 99999
Data columns (total 2 columns):
categories 100000 non-null int8
locations 100000 non-null int8
dtypes: int8(2)
memory usage: 976.6 KB
None
将转换后的数据保存到 csv
并注意该文件现在只有没有标签的数字。
文件大小也将反映此更改。
df.to_csv('test2.csv')
index,categories,locations
0,0,0
1,1,1
2,2,0
3,3,1
同时保存定义:
categories_details.to_csv('categories_details.csv')
locations_details.to_csv('locations_details.csv')
当您需要恢复文件时,从 csv
个文件加载它们:
df2 = pd.read_csv('test2.csv', index_col='index')
print df2.head()
categories locations
index
0 0 0
1 1 1
2 2 0
3 3 1
4 0 0
print df2.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 100000 entries, 0 to 99999
Data columns (total 2 columns):
categories 100000 non-null int64
locations 100000 non-null int64
dtypes: int64(2)
memory usage: 2.3 MB
None
categories_details2 = pd.read_csv('categories_details.csv', index_col='index')
print categories_details2.head()
categories
index
0 Category1
1 Category2
2 Category3
3 Category4
print categories_details2.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 4 entries, 0 to 3
Data columns (total 1 columns):
categories 4 non-null object
dtypes: object(1)
memory usage: 64.0+ bytes
None
locations_details2 = pd.read_csv('locations_details.csv', index_col='index')
print locations_details2.head()
locations
index
0 Location1
1 Location2
print locations_details2.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 2 entries, 0 to 1
Data columns (total 1 columns):
locations 2 non-null object
dtypes: object(1)
memory usage: 32.0+ bytes
None
现在使用map
将int
编码数据替换为类别描述并将其转换为Categorical
:
df2['categories'] = df2.categories.map(categories_details2.to_dict()['categories']).astype('category')
df2['locations'] = df2.locations.map(locations_details2.to_dict()['locations']).astype('category')
print df2.head()
categories locations
index
0 Category1 Location1
1 Category2 Location2
2 Category3 Location1
3 Category4 Location2
4 Category1 Location1
print df2.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 100000 entries, 0 to 99999
Data columns (total 2 columns):
categories 100000 non-null category
locations 100000 non-null category
dtypes: category(2)
memory usage: 976.6 KB
None
注意内存使用回到我们第一次将数据转换为 Categorical
时的状态。
如果你需要多次重复这个过程,自动化这个过程应该不难。
这是一种在单个 .csv 中保存包含分类列的数据框的方法:
Example:
------ -------
Fatcol Thincol: unique strings once, then numbers
------ -------
"Alberta" "Alberta"
"BC" "BC"
"BC" 2 -- string 2
"Alberta" 1 -- string 1
"BC" 2
...
The "Thincol" on the right can be saved as is in a .csv file,
and expanded to the "Fatcol" on the left after reading it in;
this can halve the size of big .csv s with repeated strings.
Functions
---------
fatcol( col: Thincol ) -> Fatcol, list[ unique str ]
thincol( col: Fatcol ) -> Thincol, dict( unique str -> int ), list[ unique str ]
Here "Fatcol" and "Thincol" are type names for iterators, e.g. lists:
Fatcol: list of strings
Thincol: list of strings or ints or NaN s
If a `col` is a `pandas.Series`, its `.values` are used.
这将 700M .csv 压缩为 248M -- 但是 write_csv
在我的 imac 上以 ~ 1 MB/sec 运行。