Pandas学习笔记:两种数据结构之Dataframe

Pandas中含有两种主要的数据结构:Series和Dataframe,本篇笔记是第2篇,记录的是Dataframe。

关于Series的介绍:https://blog.csdn.net/JT_WPC/article/details/104135662

笔记内容的结构:

  1. Dataframe简介;

  2. 构建Dataframe的几种情况;

  3. Dataframe的常用操作。

Dataframe简介

DataFrame是一个二维的含有行标记的数据结构,列之间的数据类型可以不同。同时,Dataframe可以看做是Series的容器或者是字典,其中的一个列可以看做是一个Series,是最常用的panda对象。像Series一样,DataFrame接受多种类型的输入:

  1. 一维ndarray、列表、字典或者series的字典;
  2. 二维ndarray数据;
  3. 结构化的ndarray;
  4. 另一个dataframe。

构造Dataframe时,除了传递data参数之外,还可以选择传递index(行标签)参数和columns(列标签)参数,分别表示行名和列名

同样的,dataframe中index标签和数据也是默认自动对齐,因此,当一个series字典附加上特定的index参数,将筛选出与传递的index匹配的数据。

和Series一样,使用不同的python版本会导致一些细微的不同,推荐3.6及以上版本:如果使用Python版本>= 3.6panda >= 0.23,那么当数据是一个dict,并且没有指定列时,DataFrame列将按照dict的插入顺序排序;如果您使用的是Python < 3.6panda < 0.23,并且没有指定列,那么DataFrame列将按照dictkey的字典顺序对列表排序。

构建dataframe的几种情况

情况一:从字典的字典或者是Series的字典中获取数据

由于具有多个Series,相应的,也会有多个index集合,那么最后生成的Dataframe中的index取各个Seriesindex的并集。默认dictkey的有序列表作为columns,当使用从字典中取值生成dataframe时,columns参数的作用是筛选器,选出和columns参数匹配的那部分数据,为columns中未被匹配到的部分,则会是NaN填充。

# 构建一个Series字典
In : d = {'one': pd.Series([1., 2., 3.], index=['a', 'b', 'c']),
            'two': pd.Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])}

In : df = pd.DataFrame(d)

In : df
Out: 
   one  two
a  1.0  1.0
b  2.0  2.0
c  3.0  3.0
d  NaN  4.0

In : pd.DataFrame(d, index=['d', 'b', 'a'])  # 使用index作为筛选器
Out: 
   one  two
d  NaN  4.0
b  2.0  2.0
a  1.0  1.0

In : pd.DataFrame(d, index=['d', 'b', 'a'], columns=['two', 'three'])  # 使用columns作为筛选器
Out: 
   two three
d  4.0   NaN
b  2.0   NaN
a  1.0   NaN

Dataframe的行和列的标签可以通过index属性和columns属性获取:

In : df.index
Out: Index(['a', 'b', 'c', 'd'], dtype='object')

In : df.columns
Out: Index(['one', 'two'], dtype='object')

情况二:从ndarray字典或者list字典中获取数据

所有ndarray的长度必须相同。如果传递了一个index参数,那么它的长度必须与数组的长度相同。如果没有传递index参数,则会自动生成0~ (n-1)的数字index,其中n是数组长度。

# ndarray字典的例子
# 实例化一个ndarray对象,并指定其中数据类型,i4、f4、a10分别是numpy中设定的整型、浮点型、字符串型类型名
In : data = np.zeros((2, ), dtype=[('A', 'i4'), ('B', 'f4'), ('C', 'a10')])  

#注意,这里对data进行赋值的时候,带上[:]表示向上面的结构中填充数据,而直接对data赋值表示重新实例化一个序列对象而不具有设定好的结构
In : data[:] = [(1, 2., 'Hello'), (2, 3., "World")]

In : pd.DataFrame(data)
Out: 
   A    B         C
0  1  2.0  b'Hello'
1  2  3.0  b'World'

In : pd.DataFrame(data, index=['first', 'second'])
Out: 
        A    B         C
first   1  2.0  b'Hello'
second  2  3.0  b'World'

In : pd.DataFrame(data, columns=['C', 'A', 'B'])
Out: 
          C  A    B
0  b'Hello'  1  2.0
1  b'World'  2  3.0
# list字典的例子
In : d = {'one': [1., 2., 3., 4.],
   :      'two': [4., 3., 2., 1.]}

In : pd.DataFrame(d)
Out: 
   one  two
0  1.0  4.0
1  2.0  3.0
2  3.0  2.0
3  4.0  1.0

In : pd.DataFrame(d, index=['a', 'b', 'c', 'd'])
Out: 
   one  two
a  1.0  4.0
b  2.0  3.0
c  3.0  2.0
d  4.0  1.0

情况三:从字典的列表构建Dataframe

Dataframe中一个列是一个Series,也符合在实际使用时,一个列表示一项特征,因此实例化dataframe对象时,会层层解刨数据源中的数据,最外层的字典结构的key会被转换为datafarm的columns(列标签),然后再遇到key,会被转换为index(行标签)。字典的列表也不例外,以下例子中,遇到的第一层字典的key是a,b,c,他们就是所要生成的dataframe的columns。

In : data2 = [{'a': 1, 'b': 2}, {'a': 5, 'b': 10, 'c': 20}]

In : pd.DataFrame(data2)
Out: 
   a   b     c
0  1   2   NaN
1  5  10  20.0

In : pd.DataFrame(data2, index=['first', 'second'])
Out: 
        a   b     c
first   1   2   NaN
second  5  10  20.0

In : pd.DataFrame(data2, columns=['a', 'b'])
Out: 
   a   b
0  1   2
1  5  10

情况四:从元组的词典构建DataFrame

通过传递一个元组字典来自动创建一个多索引的框架。这个例子更能体现出上面所说到的从数据源中提取columns和index的规律:一级词典的key作为columns中的元素,二级词典中的key作为index中的元素。

In : pd.DataFrame({('a', 'b'): {('A', 'B'): 1, ('A', 'C'): 2},
   :               ('a', 'a'): {('A', 'C'): 3, ('A', 'B'): 4},
   :               ('a', 'c'): {('A', 'B'): 5, ('A', 'C'): 6},
   :               ('b', 'a'): {('A', 'C'): 7, ('A', 'B'): 8},
   :               ('b', 'b'): {('A', 'D'): 9, ('A', 'B'): 10}})
Out: 
       a              b      
       b    a    c    a     b
A B  1.0  4.0  5.0  8.0  10.0
  C  2.0  3.0  6.0  7.0   NaN
  D  NaN  NaN  NaN  NaN   9.0

情况五:使用Dataframe的构造函数

1.DataFrame.from_dict

函数API传送门,顾名思义,构造DataFrame的数据来源为字典类型。它的操作类似于DataFrame构造函数,orient参数表示数据的填充方向,默认为“columns”,表示将字典中的value按照列填充,字典中的key作为列标签,将其设置为“index”时表示横向填充,字典的key作为行标签。

In : pd.DataFrame.from_dict(dict([('A', [1, 2, 3]), ('B', [4, 5, 6])]))
Out: 
   A  B
0  1  4
1  2  5
2  3  6

In : pd.DataFrame.from_dict(dict([('A', [1, 2, 3]), ('B', [4, 5, 6])]), orient='index')
Out: 
   0  1  2
A  1  2  3
B  4  5  6

同样的,看过函数的api文档之后,可以知道通过构造函数实例化dataframe对象时也可以传递columns参数,只限于在orient="index"的情况下使用,原因很简单,字典自身肯定是带有key的。

In : pd.DataFrame.from_dict(dict([('A', [1, 2, 3]), ('B', [4, 5, 6])]),
   :                        orient='index', columns=['one', 'two', 'three']) 
Out: 
   one  two  three
A    1    2      3
B    4    5      6

2.DataFrame.from_records

函数API文档传送门,from_records接受元组的列表或ndarray作为参数来构造dataframe。可以将dtype中某个字段指定为要实例化的DataFrame的index。例如下面的例子中,将C列作为index标签。

In : data
Out: 
array([(1, 2., b'Hello'), (2, 3., b'World')],
      dtype=[('A', '<i4'), ('B', '<f4'), ('C', 'S10')])

In : pd.DataFrame.from_records(data, index='C')
Out: 
          A    B
C               
b'Hello'  1  2.0
b'World'  2  3.0

Dataframe常用操作

1.索引、添加和删除

基础的索引操作如下:

操作

语法

返回结果

选择一列

df[col]

Series

通过标签选择行

df.loc[label]

Series

按整数位置选择行

df.iloc[loc]

Series

行切片

df[5:10]

DataFrame

通过布尔向量选择行

df[bool_vec]

DataFrame

例如,行选择返回一个series,其索引是DataFrame的列:

In : df
Out: 
   one  bar   flag  foo  one_trunc
a  1.0  1.0  False  bar        1.0
b  2.0  2.0  False  bar        2.0
c  3.0  3.0   True  bar        NaN
d  NaN  NaN  False  bar        NaN

In : df.loc['b']
Out: 
one              2
bar              2
flag         False
foo            bar
one_trunc        2
Name: b, dtype: object

In : df.iloc[2]
Out: 
one             3
bar             3
flag         True
foo           bar
one_trunc     NaN
Name: c, dtype: object

语义上,可以将DataFrame看作Series的字典。并且获取、设置和删除列的语法与字典的操作相同:

In : df
Out: 
   one  two
a  1.0  1.0
b  2.0  2.0
c  3.0  3.0
d  NaN  4.0

In : df['one']
Out: 
a    1.0
b    2.0
c    3.0
d    NaN
Name: one, dtype: float64

In : df['three'] = df['one'] * df['two']

In : df['flag'] = df['one'] > 2

In : df
Out: 
   one  two  three   flag
a  1.0  1.0    1.0  False
b  2.0  2.0    4.0  False
c  3.0  3.0    9.0   True
d  NaN  4.0    NaN  False

删除方面,与dict一样,列可以delpop操作:

In : del df['two']

In : three = df.pop('three')

In : df
Out: 
   one   flag
a  1.0  False
b  2.0  False
c  3.0   True
d  NaN  False

当插入一个标量值时,它会自动地按照列方向传播来填充列,也就是自动按照行进行复制:

In : df['foo'] = 'bar'

In : df
Out: 
   one   flag  foo
a  1.0  False  bar
b  2.0  False  bar
c  3.0   True  bar
d  NaN  False  bar

当插入的SeriesindexDataFrameindex不同时,它将会贴合DataFrameindex:

In : df['one_trunc'] = df['one'][:2]

In : df
Out: 
   one   flag  foo  one_trunc
a  1.0  False  bar        1.0
b  2.0  False  bar        2.0
c  3.0   True  bar        NaN
d  NaN  False  bar        NaN

可以在dataframe中插入ndarray结构的数据,但是它们的长度必须与DataFrame的index的长度匹配。默认情况下,列被插入到末尾,insert函数可用于在列中的特定位置插入。下面的例子中参数的含义:插入位置为1,列名设为‘bar’,数据为使用df[‘one’]一列的数据。

In : df.insert(1, 'bar', df['one'])

In : df
Out: 
   one  bar   flag  foo  one_trunc
a  1.0  1.0  False  bar        1.0
b  2.0  2.0  False  bar        2.0
c  3.0  3.0   True  bar        NaN
d  NaN  NaN  False  bar        NaN

在方法链中分配新列

dplyrR语言总高效处理数据的包)的mutate动词的启发,DataFrame有一个assign()方法,允许程序员使用现有的列来创建新列,Assign函数总是返回数据的一个副本,而原始的DataFrame则保持不变。

In : from sklearn.datasets import load_iris
In : iris =  pd.DataFrame(load_iris().data, columns=['SepalLength','SepalWidth','PetalLength','PetalWidth'])

In : iris.head()
Out: 
   SepalLength  SepalWidth  PetalLength  PetalWidth  
0          5.1         3.5          1.4         0.2  
1          4.9         3.0          1.4         0.2  
2          4.7         3.2          1.3         0.2  
3          4.6         3.1          1.5         0.2  
4          5.0         3.6          1.4         0.2 


In : (iris.assign(sepal_ratio=iris['SepalWidth'] / iris['SepalLength']).head())
Out: 
   SepalLength  SepalWidth  PetalLength  PetalWidth  sepal_ratio
0          5.1         3.5          1.4         0.2  0.686275
1          4.9         3.0          1.4         0.2  0.612245
2          4.7         3.2          1.3         0.2  0.680851
3          4.6         3.1          1.5         0.2  0.673913
4          5.0         3.6          1.4         0.2  0.720000

在上面的示例中,我们插入了一个预先计算的值。我们还可以传入一个单参数的函数,它的参数将在分配给它的DataFrame上求值,然后将求得的数值插入到dataframe中。

In : iris.assign(sepal_ratio=lambda x: (x['SepalWidth'] / x['SepalLength'])).head()  # iris作为参数传给x
Out: 
   SepalLength  SepalWidth  PetalLength  PetalWidth  sepal_ratio
0          5.1         3.5          1.4         0.2  0.686275
1          4.9         3.0          1.4         0.2  0.612245
2          4.7         3.2          1.3         0.2  0.680851
3          4.6         3.1          1.5         0.2  0.673913
4          5.0         3.6          1.4         0.2  0.720000

赋值的函数签名是简单的**kwargskey是新字段的列名,这些value要么是要插入的值(例如,一个SeriesNumPy数组),要么是要在DataFrame上调用的一个参数的函数。assign函数返回原始DataFrame的副本,并向其中插入新值,而不改变原始数据。Python 3.6开始,保留了**kwargs的顺序。这允许依赖赋值,也就是说,在**kwargs中后面的表达式可以引用前面在相同assign()中创建的列。

In : dfa = pd.DataFrame({"A": [1, 2, 3],
   :                     "B": [4, 5, 6]})


In : dfa.assign(C=lambda x: x['A'] + x['B'],
   :            D=lambda x: x['A'] + x['C']) 
Out: 
   A  B  C   D
0  1  4  5   6
1  2  5  7   9
2  3  6  9  12

在第二个表达式中,x['C']将指向新创建的列,它等于dfa['A'] + dfa['B']

数据对齐与算法

DataFrame对象之间的数据对齐会自动对齐列和索引(行标签)。结果对象将具有列和行标签的并集。

In : df = pd.DataFrame(np.random.randn(10, 4), columns=['A', 'B', 'C', 'D'])

In : df2 = pd.DataFrame(np.random.randn(7, 3), columns=['A', 'B', 'C'])

In : df + df2
Out: 
          A         B         C   D
0  0.045691 -0.014138  1.380871 NaN
1 -0.955398 -1.501007  0.037181 NaN
2 -0.662690  1.534833 -0.859691 NaN
3 -2.452949  1.237274 -0.133712 NaN
4  1.414490  1.951676 -2.320422 NaN
5 -0.494922 -1.649727 -1.084601 NaN
6 -1.047551 -0.748572 -0.805479 NaN
7       NaN       NaN       NaN NaN
8       NaN       NaN       NaN NaN
9       NaN       NaN       NaN NaN

DataFrameSeries之间执行操作时,默认的行为是对齐DataFrame列上的Series索引,从而按行广播。例如:

In [86]: df - df.iloc[0]
Out[86]: 
          A         B         C         D
0  0.000000  0.000000  0.000000  0.000000
1 -1.359261 -0.248717 -0.453372 -1.754659
2  0.253128  0.829678  0.010026 -1.991234
3 -1.311128  0.054325 -1.724913 -1.620544
4  0.573025  1.500742 -0.676070  1.367331
5 -1.741248  0.781993 -1.241620 -2.053136
6 -1.240774 -0.869551 -0.153282  0.000430
7 -0.743894  0.411013 -0.929563 -0.282386
8 -1.194921  1.320690  0.238224 -1.482644
9  2.293786  1.856228  0.773289 -1.446531

在处理时间序列数据的特殊情况下,如果DataFrame索引包含日期,广播将按列显示:

In : index = pd.date_range('1/1/2020', periods=8)

In : df = pd.DataFrame(np.random.randn(8, 3), index=index, columns=list('ABC'))

In : df
Out: 
                   A         B         C
2020-01-01 -0.998725 -0.254482 -0.275089
2020-01-02  1.292521 -0.670753 -0.610544
2020-01-03  0.849535 -1.602513 -1.024664
2020-01-04  1.667589  0.008778 -0.311506
2020-01-05  0.959575 -1.265163 -1.019450
2020-01-06  1.292459  0.561263  1.307999
2020-01-07  0.648400  0.869591  0.062901
2020-01-08 -0.421260  0.815654 -2.130895

In : type(df['A'])
Out: pandas.core.series.Series

In : df.sub(df['A'], axis=0)  # 不能使用 df - df['A'] 的方式计算列之间的相减
Out: 
              A         B         C
2020-01-01  0.0  0.744243  0.723636
2020-01-02  0.0 -1.963274 -1.903065
2020-01-03  0.0 -2.452048 -1.874199
2020-01-04  0.0 -1.658811 -1.979095
2020-01-05  0.0 -2.224738 -1.979025
2020-01-06  0.0 -0.731196  0.015540
2020-01-07  0.0  0.221192 -0.585499
2020-01-08  0.0  1.236914 -1.709635

Dataframe和标量的运算:

In : df * 5 + 2
Out: 
                    A         B         C
2020-01-01  -2.993623  0.727590  0.624555
2020-01-02   8.462607 -1.353766 -1.052718
2020-01-03   6.247674 -6.012566 -3.123321
2020-01-04  10.337944  2.043890  0.442469
2020-01-05   6.797874 -4.325816 -3.097251
2020-01-06   8.462296  4.806315  8.539996
2020-01-07   5.241999  6.347957  2.314507
2020-01-08  -0.106301  6.078269 -8.654477

In : 1/df
Out[10]: 
                   A           B          C
2020-01-01 -1.001277   -3.929550  -3.635188
2020-01-02  0.773682   -1.490861  -1.637885
2020-01-03  1.177115   -0.624020  -0.975930
2020-01-04  0.599668  113.920756  -3.210208
2020-01-05  1.042128   -0.790412  -0.980921
2020-01-06  0.773719    1.781696   0.764526
2020-01-07  1.542258    1.149965  15.897902
2020-01-08 -2.373830    1.226010  -0.469286

In : df ** 4
Out[11]: 
                   A             B          C
2020-01-01  0.994908  4.194002e-03   0.005727
2020-01-02  2.790942  2.024188e-01   0.138953
2020-01-03  0.520864  6.594873e+00   1.102367
2020-01-04  7.733141  5.937294e-09   0.009416
2020-01-05  0.847843  2.562042e+00   1.080100
2020-01-06  2.790406  9.923522e-02   2.927049
2020-01-07  0.176755  5.718222e-01   0.000016
2020-01-08  0.031492  4.426122e-01  20.618097

也可以使用bool运算符进行计算:

In : df1 = pd.DataFrame({'a': [1, 0, 1], 'b': [0, 1, 1]}, dtype=bool)

In : df2 = pd.DataFrame({'a': [0, 1, 1], 'b': [1, 1, 0]}, dtype=bool)

In : df1 & df2
Out: 
       a      b
0  False  False
1  False   True
2   True  False

In : df1 | df2
Out: 
      a     b
0  True  True
1  True  True
2  True  True

In : df1 ^ df2
Out: 
       a      b
0   True   True
1   True  False
2  False   True

In : -df1
Out: 
       a      b
0  False   True
1   True  False
2  False  False

转置操作

要想转置,可以访问T属性(也可以是transpose函数),和ndarray操作类似:

In : df[:5].T  # 只对前5个进行转置
Out: 
   2020-01-01  2020-01-02  2020-01-03  2020-01-04  2020-01-05
A   -0.998725    1.292521    0.849535    1.667589    0.959575
B   -0.254482   -0.670753   -1.602513    0.008778   -1.265163
C   -0.275089   -0.610544   -1.024664   -0.311506   -1.019450

Dataframe与NumPy函数的互操作性

假定其中的数据是数字,则可以逐个元素地使用NumPy ufuncs(log,exp,sqrt等)和其他各种NumPy函数,在Series和DataFrame上都不会出现问题:

In : np.exp(df)
Out[13]: 
                   A         B         C
2020-01-01  0.368349  0.775318  0.759505
2020-01-02  3.641957  0.511323  0.543056
2020-01-03  2.338559  0.201390  0.358917
2020-01-04  5.299375  1.008817  0.732343
2020-01-05  2.610586  0.282193  0.360793
2020-01-06  3.641731  1.752885  3.698766
2020-01-07  1.912478  2.385936  1.064922
2020-01-08  0.656219  2.260653  0.118731

In : np.asarray(df)
Out: 
array([[-0.99872467, -0.25448205, -0.27508894],
       [ 1.2925213 , -0.67075312, -0.6105436 ],
       [ 0.84953475, -1.60251315, -1.02466411],
       [ 1.66758887,  0.00877803, -0.31150629],
       [ 0.95957478, -1.26516326, -1.01945013],
       [ 1.29245922,  0.56126307,  1.30799924],
       [ 0.64839989,  0.86959144,  0.06290138],
       [-0.42126021,  0.81565377, -2.13089546]])

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值