Python中的重要科学计算库Pandas

Pandas库用来实现对CSV的快速处理,其在numpy之上提供了index和column机制。

类型系统

1
df.dtypes

得到

如果显示类型为object,则可能是混杂的类型

Series对象的相关性质

相关成员

1
df = pd.DataFrame([[1, 2], [3, 4]], index=['aa', 'bb'], columns=['a', 'b'])
  1. name
    表示这个Series的名字。如果Series是从pd.Series构建的,等于传入的name参数的值,默认为空。如果是从DataFrame得到的,对应的是column或者index的值。
  2. index

输入输出

输入

可以使用下面的方式实现条件加载,其中lambda chunk读取chunk这个DataFrame,判断它最后一行的id值,如果小于MAXN就继续读下一个。

1
2
3
chunks = pd.read_csv(xxx, sep=',', skiprows=0, chunksize = 200)
chunks = itertools.takewhile(lambda chunk: int(chunk['id'].iloc[-1]) < MAXN, chunks)
df = pd.concat(chunks)

【这个方案不行,chunk.index.get_indexer_for只是返回局部的index,所以还得用一个全局的tot来标记】
当然,如果没有id这一列,而只要单纯的前maxn列,则可以

1
chunks = itertools.takewhile(lambda chunk: (chunk.index.get_indexer_for(chunk.index) < maxn).any(), chunks)

其中的chunk.index.get_indexer_for可以换成

1
df.index.get_loc(index_name)

输出

1
2
with pd.option_context('display.max_rows', None, 'display.max_columns', None):
print pd

以Excel表形式输出

可以借助于pd.ExcelWriter组件来将DataFrame输出成Excel表。
首先创建一个writer

1
writer = pd.ExcelWriter(u"{}.xlsx".format(name), engine='xlsxwriter')

然后导出到一个sheet中,注意sheet_name的长度是有限制的

1
df.to_excel(writer, sheet_name=sheet_name)

我们可以通过writer对已经导入的进行进一步修改。在下面的代码中,我们创建了一个图标chart_cmp,这个图标中包含了predictreal两个系列的数据,分别位于B列的第2到42行,和C列的2到42行。

1
2
3
4
5
6
7
8
workbook  = writer.book
worksheet = writer.sheets[sheet_name]
chart_cmp = workbook.add_chart({'type': 'line'})
chart_cmp.add_series({'name': u"predict",
'values': '={}!${}${}:${}${}'.format(sheet_name, "B", 2, "B", 42)})
chart_cmp.add_series({'name': u"sreal",
'values': '={}!${}${}:${}${}'.format(sheet_name, "C", 2, "C", 42),
'y2_axis': 1})

接着,我们可以指定主要坐标轴和次要坐标轴。

1
2
3
4
chart_cmp.set_y_axis({'min': 0.4, 'max': 0.6})
chart_cmp.set_y2_axis({'min': 0, 'max': 1})
worksheet.insert_chart('F2', chart_cmp)
writer.save()

交互

原生数组和DataFrame转换

可以通过一个字典创建DataFrame,这时候传入的字典是按照列来组织的。
可以指定数据和列名(甚至index)创建DataFrame,这时候传入的数组是按照行来组织的。

1
2
3
4
5
6
df = pd.DataFrame(data=[[1,2,3],[10,20,30]], columns=["a", "b", "c"])
>>> df
>>> df
a b c
0 1 2 3
1 10 20 30

ndarray和DataFrame转换

从DataFrame到ndarray可以用

1
2
df.values
df.to_numpy()

调试

查看所有的列,注意直接输出d.columns会返回一个Index,不是简写的,所以很难看。

1
d.columns.values

不限制打印行数

1
2
3
4
pd.set_option('display.max_rows', None)
pd.set_option('display.max_columns', None)
pd.set_option('display.width', None)
pd.set_option('display.max_colwidth', -1)

索引与编号

Axis

和numpy一样,Pandas的axis表示受影响的是哪一列。
我们可以这样去记忆,假如一个三维的 Dataframe,我们求sum(axis = 1),那么必然是axis这一列没了,压成了 scalar。
往回带入到二维的情况,axis = 0时,相当于行被压没了,也就是每一列上的所有的行加起来,如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> df = pd.DataFrame([[1,2],[3,4]])
>>> df
0 1
0 1 2
1 3 4
>>> df.sum(axis=0)
0 4
1 6
dtype: int64
>>> df.sum(axis=1)
0 3
1 7
dtype: int64

对于二维数组的concat,那么就是axis=1受影响,也就是两个列拼在一起

1
pd.concat([c1, c2], axis = 1))

编号

reset_index

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
35
36
>>> df = pd.DataFrame([('bird', 389.0), ('bird', 24.0), ('mammal', 80.5), ('mammal', np.nan)],
index=['falcon', 'parrot', 'lion', 'monkey'],
columns=('class', 'max_speed'))
>>> df
class max_speed
falcon bird 389.0
parrot bird 24.0
lion mammal 80.5
monkey mammal NaN

>>> df.loc['parrot']
class bird
max_speed 24.0
Name: parrot, dtype: object

>>> df.iloc[0]
class bird
max_speed 389.0
Name: falcon, dtype: object

>>> df.reset_index()
index class max_speed
0 falcon bird 389.0
1 parrot bird 24.0
2 lion mammal 80.5
3 monkey mammal NaN

>>> df.reset_index().loc['parrot']
Traceback (most recent call last):
...

>>> df.reset_index().iloc[0]
index falcon
class bird
max_speed 389.0
Name: 0, dtype: object

选择

loc的标签选择

.loc主要有几点用法。

  1. 首先可以接受一个Label,或者一个Label数组,或者一个Range。用来索引DataFrame中的某一行

    1
    2
    3
    4
    5
    6
    7
    8
    import pandas as pd
    import numpy as np
    df = pd.DataFrame([[1, 2], [3, 4], [5,6]], index=['a', 'c', 'b'], columns=['a', 'b'])
    >>> df
    a b
    a 1 2
    c 3 4
    b 5 6

    使用loc[],得到一个Series,表示选择的row。

    1
    2
    3
    >>> df.loc["a"]
    a 1
    b 2

    使用loc[[]],得到一个DataFrame,表示所有选择的row。

    1
    2
    3
    4
    5
    6
    7
    >>> df.loc[["a"]]
    a b
    a 1 2
    >>> df.loc[["a", "b"]]
    a b
    a 1 2
    b 5 6

    可以选择从'a''c'之间的行。

    1
    2
    3
    4
    >>> df.loc['a':'c']
    a b
    a 1 2
    c 3 4
  2. 可以同时传入两个维度
    如果这里面有一个是Label,那么就会降一维,如果是一个List或者Range,那么维数就不变。
    得到一个Series

    1
    2
    3
    4
    5
    >>> df.loc['a':'b', 'a']
    a 1
    c 3
    b 5
    Name: a, dtype: int64

    得到一个DataFrame

    1
    2
    3
    4
    5
    >>> df.loc[:, ['b']]
    b
    a 2
    c 4
    b 6

    得到一个Series

    1
    2
    3
    >>> df.loc['a', ['b']]
    b 2
    Name: a, dtype: int64
  3. 我们可以传入一个Bool数组,作为Mask,表示需要哪些行和列
    下面的代码中,我们选择的是第0/2行、第1列,由于我们是通过List来调用的,所以返回的是DataFrame。

    1
    2
    3
    4
    >>> df.loc[[True, False, True], [False, True]]
    b
    a 2
    b 6

    我们也可以指定一个较短的数组,后面会默认是False。同样得到DataFrame。

    1
    2
    3
    >>> df.loc[[True, False], [False, True]] 
    b
    a 2
  4. 对Series的补充说明
    此外,loc也可以用在Series上,这时候我们得到Scalar

    1
    2
    s.loc['B'] # 得到一个numpy.float64
    s['B'] # 得到一个numpy.float64

iloc

相比loc,iloc提供了通过从绝对位置开始,而不是根据label的索引方式。

中括号__getitem__

__getitem__的作用和loc有差别。我们进行下面的比较。
可以发现,当传入一个Label时,loc默认这个Label是行,而__getitem__默认是列

1
2
3
4
5
>>> df = pd.DataFrame([[1, 2], [3, 4]], index=['a', 'b'], columns=['a', 'b'])
>>> df
a b
a 1 2
b 3 4

对于loc,返回的是a这一行

1
2
3
4
>>> df.loc["a"]
a 1
b 2
Name: a, dtype: int64

对于__getitem__,返回的是a这一列

1
2
3
>>> df["a"]
a 1
b 3

当我们传入一个List of Labels的时候,情况也是相同的。

1
2
3
4
5
6
7
8
>>> df.loc[["a"]]
a b
a 1 2
>>> df[["a"]]
a
a 1
b 3
Name: a, dtype: int64

条件选择

可以通过以下的方式进行条件选择,或者判断有多少项满足条件。

借助于Bool数组

一个简单的方法就是借助于Bool数组去Mask,这也是数据处理中常见到的用法。

1
2
3
4
5
6
>>> df['a'] == 1
a True
b False
>>> df.loc[df['a'] == 1]
a b
a 1 2

通过下面的方法可以计算DataFrame某一列中满足某些条件的有多少个

1
2
3
4
5
6
7
8
9
10
11
df = pd.DataFrame({"class":[1,1,1,2,2], "value":[1,2,3,4,5]})
# 或者df[df['class']==1],见后面对`__getitem__`的讨论
>>> df[df.loc[:,'class']==1]
class value
0 1 1
1 1 2
2 1 3
>>> df[df.loc[:,'class']==1].sum()
class 3
value 6
dtype: int64

借助于where

当我们需要计算Series的某一行满足条件的有多少个时,可以使用where来做。下面语句的意思是过滤df里面大于1的值,如果有不满足条件的,那么就将它的值改为100

1
df.where(df > 1, 100)

特别地,where还有简化版,下面两个函数分别将未匹配的和匹配的改为NaN。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
s = pd.Series(range(5))
>>> s.where(s > 0)
0 NaN
1 1.0
2 2.0
3 3.0
4 4.0
dtype: float64
>>> s.mask(s > 0)
0 0.0
1 NaN
2 NaN
3 NaN
4 NaN
dtype: float64

因此我们可以用下面的语句来计算满足条件的个数

1
s.where(condition).count()

复杂条件选择

可以使用&|等符号。
需要注意,Python中的in不再使用,需要替换成.isin。例如判断groupfield系列里面是否有值是在lst之中的。

1
df['field'].isin(lst)

当需要判断单个值的时候会简单点,例如判断在groupfield系列里面是否存在0这个值。

1
0 in group['field'].values

ix

得到一个Series

1
df.ix[0]

比较

在条件选择时涉及比较问题,我们分类讨论,设置

1
df = pd.DataFrame([[1, 2], [3, 4]], index=['a', 'b'], columns=['a', 'b'])

与None比较

在比较

1
df == None

会产生如下的错误,所以

1
TypeError: Could not compare [None] with block values

要使用

1
df is None

这也是在Python中常常需要注意的一点

使用Callable

1
2
3
4
5
6
>>> df.loc[lambda df: df.a > 2, :]
a b
b 3 4
>>> df.loc[lambda df: df["a"] > 2, lambda df: df["b"] > 3]
b
b 4

使用Apply/Transform

对于更复杂的情况,我们可以借助于DataFrame.apply,它能接收一个自定义化的函数。具体参考下文。

增删改查

数组合并/连接

append和concate默认是纵向连接,也就是结果中column是不变的,但是row变多了。merge默认是横向连接,也就是column变多了,但row不变。

append

在使用concat/append进行数组连接时候,需要特别注意索引的问题。如果我们希望实现类似numpy一样的数组合并,那么最好对合并的每一项都进行.reset_index(drop=True)

下面的代码会报错TypeError: Can only append a Series if ignore_index=True or if the Series has a name

1
2
3
4
5
df = pd.DataFrame({'A' : [1, 2, 3], 'B': [4, 5, 6]}, index = [1, 2, 3])
df2 = pd.DataFrame({'A' : [1, 2, 3], 'B': [4, 5, 6]})
ser = pd.Series([7, 8])
df.append(ser)
df2.append(ser)

那加上name可以么?可以发现,append是默认添加一行的,但我们的Series不带columns属性。所以尽管我们预期的是将7和8添加到66行的A和B列上,但实际上新的DataFrame多了两列。

1
2
3
4
5
6
7
>>> ser = pd.Series([7, 8], name=66)
>>> df.append(ser)
A B 0 1
0 1.0 4.0 NaN NaN
1 2.0 5.0 NaN NaN
2 3.0 6.0 NaN NaN
66 NaN NaN 7.0 8.0

于是解决方案应运而生,重置一下Column就可以了。这个方法就是将矩阵转置,然后去重置Index,然后再转置回来。

1
2
3
4
5
6
7
>>> df = df.T.reset_index(drop=True).T
>>> df.append(ser)
0 1
0 1 4
1 2 5
2 3 6
66 7 8

事实上构造一个DataFrame会更简单。但是由于DataFrame的构造函数在这里是以列为基础的,所以下面的语句写起来并不舒服。

1
2
3
4
5
6
7
>>> delta_df = pd.DataFrame({'A': [7], 'B' : [8]}, index=[66])
>>> df.append(delta_df)
A B
1 1 4
2 2 5
3 3 6
66 7 8

特别地,当我们添加的Series长度和原来的DataFrame不同时,会自动进行扩展。

1
2
3
4
5
6
7
8
9
>>> df = pd.DataFrame(np.random.randn(1, 3)).T.reset_index(drop=True).T
>>> df.append(pd.Series([5]).reset_index(drop=True), ignore_index=True)
0 1 2
0 -1.737655 0.016943 -1.131891
1 5.000000 NaN NaN
>>> df.append(pd.Series([5,6,7,8]).reset_index(drop=True), ignore_index=True)
0 1 2 3
0 -1.737655 0.016943 -1.131891 NaN
1 5.000000 6.000000 7.000000 8.0

concate

另一个用来做数据合并的是concat,它相当于是append的升级版。我们最好将concat理解成关于Index的Join而不是简单的拼接。
pd.concate可以指定ignore_index参数控制是否对concate之后的数组重新编号。

merge

join

增加

append

删除

drop

可以通过drop来删除列,这样的删除不是inplace的。当然,我们也可以指定inplace参数。

1
2
df = pd.DataFrame([[1, 2], [3, 4], [5,6]], index=['a', 'c', 'b'], columns=['a', 'b'])
df.drop(columns=["a"])

注意上面的columns不能省略,也不能直接通过指定axis=...的方式省略,否则会出现下面的错误。

1
KeyError: "['a'] not found in axis".

同理,我们可以用下面的命令删除行

1
2
df.drop(index=["a", "b"])
df.drop("a", axis = 0)

pop

这个函数是以Series为格式返回被pop的列,同时inplace地去掉被pop的列。这个函数可定制范围比较小,不能接受数组。

1
2
3
4
5
6
7
8
9
10
11
>>> df = pd.DataFrame([[1, 2], [3, 4], [5,6]], index=['a', 'c', 'b'], columns=['a', 'b'])
>>> df.pop("a")
a 1
c 3
b 5
Name: a, dtype: int64
>>> df
b
a 2
c 4
b 6

遍历

可以使用iteritems来遍历Series

1
2
3
4
5
6
7
8
9
10
ser = pd.Series(np.random.randn(4), index = [2, 3, 4, 5])
for x in ser.iteritems():
print x
```Python
返回得到`(index, scalar)`组成的tuple。
```Python
(2L, -0.6139561000254863)
(3L, 0.5896349094946916)
(4L, 0.38098846567708866)
(5L, -0.5556287680764356)

下面的代码对DataFrame按列进行遍历

1
2
3
df = pd.DataFrame(np.random.randn(2, 2), columns=['A', 'B'], index = [2, 3])
for x in df.iteritems():
print x

返回得到(column, Series)组成的tuple。

1
2
3
4
5
6
('A', 2    0.637096
3 1.376316
Name: A, dtype: float64)
('B', 2 -0.698115
3 1.403478
Name: B, dtype: float64)

对GroupBy对象进行遍历的结果情形将在GroupBy中单独讨论。

替换

根据Pattern替换

可以使用to_replace函数

查找替换

swap

可以借助于loc来实现列与列之间的交换,如下

1
df.loc[:, [col1, col2]] = df.loc[:, [col2, col1]].values

Map系列操作

基于Apply

Demo设置

1
df = pd.DataFrame([[1, 2], [3, 4]], index=['a', 'b'], columns=['a', 'b'])

Apply的声明如下

1
DataFrame.apply(self, func, axis=0, raw=False, result_type=None, args=(), **kwds)[source]

其中:

  1. raw为True表示传入的是ndarray,否则是pd.Series
  2. result_type

通过Apply查找

DataFrame.apply函数方法接收一个axis,表示这个函数会对哪一个轴去apply。例如在下面的语句中,当axis为1时,就是对列去apply。这时候就会遍历所有的行,对于每一行row的所有列去应用lambda函数。

1
2
3
4
def P(x):
print("--->", x)
return True
df[df.apply(P, axis=1)]

可以看到,每一次遍历是针对的一行中的所有的列。

1
2
3
4
5
6
7
8
9
('--->', a    1
b 2
Name: a, dtype: int64)
('--->', a 3
b 4
Name: b, dtype: int64)
a b
a 1 2
b 3 4

如果我们需要找到所有的行中,对应row['a']的值在[3, 4]中的所有row,那么可以使用下面的方法。

1
2
3
>>> df[df.apply(lambda row: row['a'] in [3, 4], axis=1)] # 得到一个DataFrame
a b
b 3 4

通过Apply变换

在找到之后,我们就可以单独对这些列进行变换,例如我们需要将每一列中所有大于等于2的class的值设置为0,除了where,还可以用下面的办法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
df = pd.DataFrame({"class":[1,1,1,2,2], "value":[1,2,3,4,5]})
def handle(row):
if row["class"] >= 2:
return row
else:
row["class"] = 0
return row

>>> df.apply(handle, axis = 1)
class value
0 0 1
1 0 2
2 0 3
3 2 4
4 2 5

上面的做法实际上是对每一个row去map,修改对应的column项目的值,那么能不能把column选出来进行apply呢?执行下面的函数

1
2
3
4
5
def handle_item(item):
if item >= 2:
return item
return 0
df["class"].apply(handle_item)

我们发现输出如下,并且再打印df,也是和原来一样的,所以apply不是inplace的。

1
2
3
4
5
6
0    0
1 0
2 0
3 2
4 2
Name: class, dtype: int64

实现安全除法

我们可以基于apply实现安全除法,当除数是0时,设置默认值0.5

1
2
3
4
5
6
7
8
9
10
df = pd.DataFrame({"a":[1,1], "b":[0,2]})
def sdiv(row):
if row["b"] == 0:
return pd.Series([0.5])
return pd.Series(row["a"] / row["b"])

>>> df.apply(sdiv, axis = 1)
0
0 0.5
1 0.0

注意Series.apply没有axis参数,所以如果传入的话会报错

1
TypeError: <lambda>() got an unexpected keyword argument 'axis'

在Apply过程中获得全局信息

有的时候,我们需要知道回调函数中传入的row的的相对Index和绝对Index,这个如何来做呢?

Transform

Aggregate/Reduce系列操作

下面的方法可以求出某些列的值

1
2
3
4
5
df = pd.DataFrame([[1,2,3],[10,20,30]], columns=["a", "b", "c"])
>>> df[["a", "c"]].sum(axis = 1)
0 4
1 40
dtype: int64

或者我们可以借助apply

1
2
3
4
>>> df[["a", "c"]].apply(lambda x: x.sum(), axis=1)
0 4
1 40
dtype: int64

groupby

在Pandas中,对整行和整列进行处理是简单的,但有时我们需要对局部的行或者列进行某些处理,而对另外的行或者列进行另外的处理。一个简单的思路是将需要分开处理的逻辑放到不同的group里面进行处理,除了filter出来之外,另一种方法是groupby

相关参数

1
df = pd.DataFrame([[1, 2], [2, 3], [1, 4], [1, 3]], index=['aa', 'bb', 'cc', 'dd'], columns=['a', 'b'])
1
DataFrame.groupby(self, by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False, observed=False, **kwargs)
  1. group_keys
  2. as_index
    默认为False。

groupby的返回值是DataFrameGroupBy或者SeriesGroupBy,这两个对象都继承了GroupBy

(DataFrame/Series)GroupBy

相关字段

  1. ngroups
    获知到底有多少个group产生。
    1
    2
    3
    4
    5
    >>> g.ngroups
    2

    >>> g.groups
    {(1L, 2L): Int64Index([0], dtype='int64'), (3L, 4L): Int64Index([1], dtype='int64')}

DataFrameGroupBy

DataFrameGroupBy的文档可参考该链接

遍历

groupby会返回一个GroupBy对象,我们可以遍历它,得到的每一项是(key, group)这样的tuple:

  1. key表示用来groupby的key,如果选择一个key来groupby,那么key就是一个scalar,否则,key就是一个tuple。
  2. group是一个DataFrame。
1
2
3
4
5
6
7
8
>>> df = pd.DataFrame([[1,2],[3,4]], columns=["a", "b"])
>>> df
a b
0 1 2
1 3 4
>>> g = df.groupby(["a"])
>>> g
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x0000000005E53668>

遍历了g,可以看出g中仍然包含了a这个column

1
2
3
4
5
6
7
>>> for i in g:
... print(i)
...
(1L, a b
0 1 2)
(3L, a b
1 3 4)

还可以指定多个column

1
2
3
4
5
6
7
8
>>> g = df.groupby(["a", "b"])
>>> for i in g:
... print i
...
((1, 2), a b
0 1 2)
((3, 4), a b
1 3 4)

编号

组内编号

还有一个常见场景是对GroupBy之后的每个组内的元素进行编号
可以通过GroupBy.cumcount函数来实现。

1
2
3
4
5
6
7
8
>>> df = pd.DataFrame({'A': [1, 3, 5, 7, 9], 'B': [1, 1, 2, 2, 2]})
>>> df.groupby(['B']).cumcount()
0 0
1 1
2 0
3 1
4 2
dtype: int64

cumcount可以看作是

1
self.apply(lambda x: pd.Series(np.arange(len(x)), x.index))

组间编号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> g.ngroup()
0 0
1 0
2 1
3 1
4 1
dtype: int64

>>> df['gid'] = df.groupby(['B']).ngroup()
>>> df
A B gid
0 1 1 0
1 3 1 0
2 5 2 1
3 7 2 1
4 9 2 1

过滤

可以基于DataFrameGroupBy.filter进行过滤,但得到的就是一个DataFrame,原来的GroupBy语义会被去掉。

map

1
2
3
4
>>> def pt(x):
... print(type(x))
...
>>> g.apply(pt)

结果如下所示,可以发现不包含 group key 了。

1
2
3
4
5
<class 'pandas.core.frame.DataFrame'>
<class 'pandas.core.frame.DataFrame'>
Empty DataFrame
Columns: []
Index: []

聚合

1
2
3
4
5
6
7
>>> df = pd.DataFrame({'A': [1, 3, 5, 7, 9], 'B': [1, 1, 2, 2, 2]})
>>> g = df.groupby(['B'])
>>> g.agg('min')
A
B
1 1
2 5

随机算法

取样

复杂数据转换

分桶

1
2
>>> df = pd.DataFrame({'A': [1, 3, 5, 7, 9]})
>>> buckets = pd.qcut(df['A'], q=2)

返回的 buckets 是个关于 Interval 的 Series。

1
2
3
4
5
6
7
8
>>> for interval in buckets:
... print("{} left {} mid {} right {}".format(interval, interval.left, interval.mid, interval.right))

(0.999, 5.0] left 0.999 mid 2.9995 right 5.0
(0.999, 5.0] left 0.999 mid 2.9995 right 5.0
(0.999, 5.0] left 0.999 mid 2.9995 right 5.0
(5.0, 9.0] left 5.0 mid 7.0 right 9.0
(5.0, 9.0] left 5.0 mid 7.0 right 9.0

Interval 可以和 GroupBy 结合

1
2
3
4
5
6
7
>>> g = df.groupby(buckets)
>>> g.count()

A
A
(0.999, 5.0] 3
(5.0, 9.0] 2

可以遍历

1
2
3
4
5
6
7
8
9
10
>>> for i in g:
... print(i)
...
(Interval(0.999, 5.0, closed='right'), A
0 1
1 3
2 5)
(Interval(5.0, 9.0, closed='right'), A
3 7
4 9)

可以结合之前说的 ngroups 来实现分桶之后映射到组

1
2
3
4
5
6
7
8
>>> df['gid'] = df.groupby([buckets]).ngroup()
>>> df
A gid
0 1 0
1 3 0
2 5 0
3 7 1
4 9 1

onehot

1
2
3
4
5
6
7
8
9
>>> df = pd.DataFrame({'A': [1, 3, 5, 7, 9], 'B': [1, 1, 2, 2, 2]})
>>> pd.get_dummies(df['A'], prefix='A_')

A__1 A__3 A__5 A__7 A__9
0 1 0 0 0 0
1 0 1 0 0 0
2 0 0 1 0 0
3 0 0 0 1 0
4 0 0 0 0 1

Reference

  1. https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html
  2. https://pandas.pydata.org/pandas-docs/stable/reference/groupby.html?highlight=dataframegroupby
  3. https://pandas.pydata.org/pandas-docs/stable/user_guide/merging.html
  4. https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.html
  5. https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.where.html
  6. https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.apply.html