pandas 和类别替换
pandas and category replacement
我正在尝试通过用较短的分类值替换冗长的字段来减小约 300 个 csv 文件(大约十亿行)的大小。
我正在使用 pandas,并且我遍历了每个文件以构建一个包含 所有 个唯一值的数组我'我正在尝试更换。我不能单独在每个文件上使用 pandas.factorize,因为我需要(例如)'3001958145' 映射到 file1.csv 和 file244.csv 上的相同值。我已经创建了一个数组,我想用 替换这些值 只是通过创建另一个递增整数数组。
In [1]: toreplace = data['col1'].unique()
Out[1]: array([1000339602, 1000339606, 1000339626, ..., 3001958145, 3001958397,
3001958547], dtype=int64)
In [2]: replacewith = range(0,len(data['col1'].unique()))
Out[2]: [0, 1, 2,...]
现在,我如何有效地交换我的 'replacewith' 变量,以便为我需要遍历的每个文件的每个相应 'toreplace' 值交换?
与 pandas 处理类别一样有能力,我假设 有 是一种可以实现这一点的方法,我只是可以'找不到。我编写的用于执行此操作的函数(它依赖于 pandas.factorized 输入而不是我上面描述的排列),但它依赖于替换函数并遍历系列,所以它非常慢。
def powerreplace(pdseries,factorized):
i = 0
for unique in pdseries.unique():
print '%i/%i' % (i,len(pdseries.unique()))
i=i+1
pdseries.replace(to_replace=unique,
value=np.where(factorized[1]==unique)[0][0],
inplace=True)
谁能推荐一个更好的方法来做这件事?
这至少需要 pandas 0.15.0; (但是 .astype
语法在 0.16.0 中更加友好,所以最好使用它)。这是 docs for categoricals
进口
In [101]: import pandas as pd
In [102]: import string
In [103]: import numpy as np
In [104]: np.random.seed(1234)
In [105]: pd.set_option('max_rows',10)
创建样本集以创建一些数据
In [106]: uniques = np.array(list(string.ascii_letters))
In [107]: len(uniques)
Out[107]: 52
创建一些数据
In [109]: df1 = pd.DataFrame({'A' : uniques.take(np.random.randint(0,len(uniques)/2+5,size=1000000))})
In [110]: df1.head()
Out[110]:
A
0 p
1 t
2 g
3 v
4 m
In [111]: df1.A.nunique()
Out[111]: 31
In [112]: df2 = pd.DataFrame({'A' : uniques.take(np.random.randint(0,len(uniques),size=1000000))})
In [113]: df2.head()
Out[113]:
A
0 I
1 j
2 b
3 A
4 m
In [114]: df2.A.nunique()
Out[114]: 52
所以我们现在有 2 个要分类的帧;第一帧恰好少于完整的类别集。这是故意的;您不必事先知道完整的设置。
将 A 列转换为分类的 B 列
In [116]: df1['B'] = df1['A'].astype('category')
In [118]: i = df1['B'].cat.categories
In [124]: i
Out[124]: Index([u'A', u'B', u'C', u'D', u'E', u'a', u'b', u'c', u'd', u'e', u'f', u'g', u'h', u'i', u'j', u'k', u'l', u'm', u'n', u'o', u'p', u'q', u'r', u's', u't', u'u', u'v', u'w', u'x', u'y', u'z'], dtype='object')
如果我们迭代处理这些帧,我们从第一个开始。为了得到每一个连续的,我们添加了与现有集合的对称差异。这使类别保持相同的顺序,因此当我们因式分解时,我们得到相同的编号方案。
In [119]: cats = i.tolist() + i.sym_diff(df2['A'].astype('category').cat.categories).tolist()
我们现在已经找回了原来的那一套
In [120]: (np.array(sorted(cats)) == sorted(uniques)).all()
Out[120]: True
将下一帧 B 列设置为分类,但我们指定了类别,因此在分解时使用相同的值
In [121]: df2['B'] = df2['A'].astype('category',categories=cats)
为了证明这一点,我们 select 每个代码(分解图)。这些代码匹配; df2 有一个额外的代码(因为 Z 在第二帧而不是第一帧)。
In [122]: df1[df1['B'].isin(['A','a','z','Z'])].B.cat.codes.unique()
Out[122]: array([30, 0, 5])
In [123]: df2[df2['B'].isin(['A','a','z','Z'])].B.cat.codes.unique()
Out[123]: array([ 0, 30, 5, 51])
然后您可以简单地存储代码来代替对象 dtyped 数据。
请注意,将这些序列化为 HDF5 实际上非常有效,因为分类是本地存储的,请参阅 here
请注意,我们正在创建一种非常节省内存的方式来存储此数据。请注意 [154] 中的内存使用情况,object
dtype 实际上字符串越长越多,因为这只是指针的内存;实际值存储在堆上。虽然 [155] 是所有使用的内存。
In [153]: df2.dtypes
Out[153]:
A object
B category
dtype: object
In [154]: df2.A.to_frame().memory_usage()
Out[154]:
A 8000000
dtype: int64
In [155]: df2.B.to_frame().memory_usage()
Out[155]:
B 1000416
dtype: int64
首先,让我们创建一些随机 'categorical' 数据。
# Create some data
random_letters = list('ABCDEFGHIJ')
s_int = pd.Series(np.random.random_integers(0, 9, 100))
s = pd.Series([random_letters[i] for i in s_int])
>>> s.unique()
array(['J', 'G', 'D', 'C', 'F', 'B', 'H', 'A', 'I', 'E'], dtype=object)
现在我们将创建唯一类别到整数的映射。]
# Create a mapping of integers to the relevant categories.
mapping = {k: v for v, k in enumerate(s.unique())}
>>> mapping
{'A': 7,
'B': 5,
'C': 3,
'D': 2,
'E': 9,
'F': 4,
'G': 1,
'H': 6,
'I': 8,
'J': 0}
然后我们使用列表理解将类别替换为其映射的整数(下划线赋值表示未使用的虚拟变量)。
_ = [s.replace(cat, mapping[cat], inplace=True) for cat in mapping]
>>> s.head()
0 0
1 1
2 2
3 3
4 4
dtype: int64
如果您希望逆向过程并获得原始类别:
reverse_map = {k: v for v, k in mapping.iteritems()}
reverse_map
{0: 'J',
1: 'G',
2: 'D',
3: 'C',
4: 'F',
5: 'B',
6: 'H',
7: 'A',
8: 'I',
9: 'E'}
_ = [s.replace(int, reverse_map[int], inplace=True) for int in reverse_map]
>>> s.head()
0 J
1 G
2 D
3 C
4 F
dtype: object
我正在尝试通过用较短的分类值替换冗长的字段来减小约 300 个 csv 文件(大约十亿行)的大小。
我正在使用 pandas,并且我遍历了每个文件以构建一个包含 所有 个唯一值的数组我'我正在尝试更换。我不能单独在每个文件上使用 pandas.factorize,因为我需要(例如)'3001958145' 映射到 file1.csv 和 file244.csv 上的相同值。我已经创建了一个数组,我想用 替换这些值 只是通过创建另一个递增整数数组。
In [1]: toreplace = data['col1'].unique()
Out[1]: array([1000339602, 1000339606, 1000339626, ..., 3001958145, 3001958397,
3001958547], dtype=int64)
In [2]: replacewith = range(0,len(data['col1'].unique()))
Out[2]: [0, 1, 2,...]
现在,我如何有效地交换我的 'replacewith' 变量,以便为我需要遍历的每个文件的每个相应 'toreplace' 值交换?
与 pandas 处理类别一样有能力,我假设 有 是一种可以实现这一点的方法,我只是可以'找不到。我编写的用于执行此操作的函数(它依赖于 pandas.factorized 输入而不是我上面描述的排列),但它依赖于替换函数并遍历系列,所以它非常慢。
def powerreplace(pdseries,factorized):
i = 0
for unique in pdseries.unique():
print '%i/%i' % (i,len(pdseries.unique()))
i=i+1
pdseries.replace(to_replace=unique,
value=np.where(factorized[1]==unique)[0][0],
inplace=True)
谁能推荐一个更好的方法来做这件事?
这至少需要 pandas 0.15.0; (但是 .astype
语法在 0.16.0 中更加友好,所以最好使用它)。这是 docs for categoricals
进口
In [101]: import pandas as pd
In [102]: import string
In [103]: import numpy as np
In [104]: np.random.seed(1234)
In [105]: pd.set_option('max_rows',10)
创建样本集以创建一些数据
In [106]: uniques = np.array(list(string.ascii_letters))
In [107]: len(uniques)
Out[107]: 52
创建一些数据
In [109]: df1 = pd.DataFrame({'A' : uniques.take(np.random.randint(0,len(uniques)/2+5,size=1000000))})
In [110]: df1.head()
Out[110]:
A
0 p
1 t
2 g
3 v
4 m
In [111]: df1.A.nunique()
Out[111]: 31
In [112]: df2 = pd.DataFrame({'A' : uniques.take(np.random.randint(0,len(uniques),size=1000000))})
In [113]: df2.head()
Out[113]:
A
0 I
1 j
2 b
3 A
4 m
In [114]: df2.A.nunique()
Out[114]: 52
所以我们现在有 2 个要分类的帧;第一帧恰好少于完整的类别集。这是故意的;您不必事先知道完整的设置。
将 A 列转换为分类的 B 列
In [116]: df1['B'] = df1['A'].astype('category')
In [118]: i = df1['B'].cat.categories
In [124]: i
Out[124]: Index([u'A', u'B', u'C', u'D', u'E', u'a', u'b', u'c', u'd', u'e', u'f', u'g', u'h', u'i', u'j', u'k', u'l', u'm', u'n', u'o', u'p', u'q', u'r', u's', u't', u'u', u'v', u'w', u'x', u'y', u'z'], dtype='object')
如果我们迭代处理这些帧,我们从第一个开始。为了得到每一个连续的,我们添加了与现有集合的对称差异。这使类别保持相同的顺序,因此当我们因式分解时,我们得到相同的编号方案。
In [119]: cats = i.tolist() + i.sym_diff(df2['A'].astype('category').cat.categories).tolist()
我们现在已经找回了原来的那一套
In [120]: (np.array(sorted(cats)) == sorted(uniques)).all()
Out[120]: True
将下一帧 B 列设置为分类,但我们指定了类别,因此在分解时使用相同的值
In [121]: df2['B'] = df2['A'].astype('category',categories=cats)
为了证明这一点,我们 select 每个代码(分解图)。这些代码匹配; df2 有一个额外的代码(因为 Z 在第二帧而不是第一帧)。
In [122]: df1[df1['B'].isin(['A','a','z','Z'])].B.cat.codes.unique()
Out[122]: array([30, 0, 5])
In [123]: df2[df2['B'].isin(['A','a','z','Z'])].B.cat.codes.unique()
Out[123]: array([ 0, 30, 5, 51])
然后您可以简单地存储代码来代替对象 dtyped 数据。
请注意,将这些序列化为 HDF5 实际上非常有效,因为分类是本地存储的,请参阅 here
请注意,我们正在创建一种非常节省内存的方式来存储此数据。请注意 [154] 中的内存使用情况,object
dtype 实际上字符串越长越多,因为这只是指针的内存;实际值存储在堆上。虽然 [155] 是所有使用的内存。
In [153]: df2.dtypes
Out[153]:
A object
B category
dtype: object
In [154]: df2.A.to_frame().memory_usage()
Out[154]:
A 8000000
dtype: int64
In [155]: df2.B.to_frame().memory_usage()
Out[155]:
B 1000416
dtype: int64
首先,让我们创建一些随机 'categorical' 数据。
# Create some data
random_letters = list('ABCDEFGHIJ')
s_int = pd.Series(np.random.random_integers(0, 9, 100))
s = pd.Series([random_letters[i] for i in s_int])
>>> s.unique()
array(['J', 'G', 'D', 'C', 'F', 'B', 'H', 'A', 'I', 'E'], dtype=object)
现在我们将创建唯一类别到整数的映射。]
# Create a mapping of integers to the relevant categories.
mapping = {k: v for v, k in enumerate(s.unique())}
>>> mapping
{'A': 7,
'B': 5,
'C': 3,
'D': 2,
'E': 9,
'F': 4,
'G': 1,
'H': 6,
'I': 8,
'J': 0}
然后我们使用列表理解将类别替换为其映射的整数(下划线赋值表示未使用的虚拟变量)。
_ = [s.replace(cat, mapping[cat], inplace=True) for cat in mapping]
>>> s.head()
0 0
1 1
2 2
3 3
4 4
dtype: int64
如果您希望逆向过程并获得原始类别:
reverse_map = {k: v for v, k in mapping.iteritems()}
reverse_map
{0: 'J',
1: 'G',
2: 'D',
3: 'C',
4: 'F',
5: 'B',
6: 'H',
7: 'A',
8: 'I',
9: 'E'}
_ = [s.replace(int, reverse_map[int], inplace=True) for int in reverse_map]
>>> s.head()
0 J
1 G
2 D
3 C
4 F
dtype: object