【pandas数据分析】pandas数据结构

文章目录

  • Series
    • 创建Series
      • 从dict创建
      • 从ndarray创建
      • 从标量创建
    • Series的特性
      • 类ndarray
      • 类dict
    • 向量化操作与标签对齐
    • 名称属性
  • DataFrame
    • 创建DataFrame
      • 从Series的字典创建
      • 从字典对象的字典创建
      • 从ndarray或列表的字典创建
      • 从结构化或记录数组创建
      • 从字典列表创建
      • 从元组字典创建
      • 从一个Series创建
      • 从命名元组列表创建
      • 从数据类列表中创建
      • 其他创建方式
        • DataFrame.from\_dict
        • DataFrame.from\_records
    • DataFrame操作
      • 列的选择、添加和删除
      • 在方法链中分配新列
      • 索引/选择
      • 数据对齐和算数操作
      • 转置
      • DataFrame与NumPy函数的互操作

众所周知,数据结构在类库、编程语言甚至是整个计算机科学中都是极其重要的存在,它决定了数据的表达和承载能力、对数据的处理和操作的灵活度和高效性,也是一个类库或一门语言强大功能的其中一种表现。对于pandas这样一个专门做数据分析的类库而言,数据结构无疑是整个工具的基石,所有强大的功能和操作都是基于其数据结构实现的。前面的文章(pandas概述)中简单提到了pandas中主要有两种数据结构:

  • 用于表示一维数据的Series

  • 用于表示二维数据的DataFrame

在这里,我们对这两种数据结构做进一步的了解。

按照惯例,现在notebook或者IPython中导入NumPy和pandas:

import numpy as np
import pandas as pd

Series

Series是一维的带有标签的数组,能够保存任何数据类型的数据(整数、字符串、浮点数、Python对象等)。Series中每个元素都对应一个标签,这些标签统称为索引

【pandas数据分析】pandas数据结构_第1张图片

备注:虽然一个Series中的元素可以是不同数据类型的(异构),但通常情况下很少这么使用,大部分情况下其中的元素都是具有相同数据类型的(同质),所以当提到Series时,没有特别说明的话,一般指的是同质的Series

创建Series

创建Series的基本方法如下:

s = pd.Series(data, [index=index])

这里,data可以是很多不同的数据类型:

  • Python字典

  • ndarray

  • 标量

传递的index是一个标签的列表,该参数是可选的,最终如何生成索引则取决于data是什么类型的数据。

从dict创建

In [3]: d = {'b': 1, 'a': 0, 'c': 2}

In [4]: pd.Series(d)
Out[4]:
b    1
a    0
c    2
dtype: int64

从上面的代码和输出可以看出,当没有传递index参数时,Series的索引为字典的key,索引对应的值为字典的value。另外根据官方文档的说法,如果Python版本≥3.6且pandas版本≥0.23,那么索引将按dict的插入顺序排序。如果Python版本<3.6或pandas版本<0.23,则索引将按字典键的字母顺序排序。

如果指定了index,则只有和index中的标签对应的字典中的值才会被取出用于创建Series,如果某个标签不是字典的键,则这个标签对应的值为NaN(not a number,在pandas中用于标记缺失数据)

In [5]: pd.Series(d, index=['b', 'c', 'd', 'a'])
Out[5]:
b    1.0
c    2.0
d    NaN
a    0.0
dtype: float64

从ndarray创建

如果data是一个ndarray,如果要指定index,那么其长度必须和data长度一样。如果没有传递index,则会自动创建一个整数索引,其值为[0, ..., len(data) - 1]

In [6]: s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])

In [7]: s
Out[7]:
a    0.554556
b    0.153393
c   -0.052892
d    0.153844
e   -0.428457
dtype: float64

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

In [9]: pd.Series(np.random.randn(5))
Out[9]:
0   -0.189954
1   -1.135869
2    0.034719
3    1.603313
4    0.654774
dtype: float64

从标量创建

如果data是标量值,则必须提供索引。该值将会重复以匹配索引的长度。

In [10]: pd.Series(5.0, index=['a', 'b', 'c', 'd', 'e'])
Out[10]:
a    5.0
b    5.0
c    5.0
d    5.0
e    5.0
dtype: float64

Series的特性

类ndarray

Series的行为和ndarray非常类似,它是大多数NumPy函数的有效参数。但是,切片等操作也会对索引进行切片。

In [11]: s
Out[11]:
a    0.554556
b    0.153393
c   -0.052892
d    0.153844
e   -0.428457
dtype: float64

In [12]: s[0]
Out[12]: 0.5545560665697387

In [13]: s[:3]
Out[13]:
a    0.554556
b    0.153393
c   -0.052892
dtype: float64

In [14]: s[s > s.median()]
Out[14]:
a    0.554556
d    0.153844
dtype: float64

In [15]: s[[4, 3, 1]]
Out[15]:
e   -0.428457
d    0.153844
b    0.153393
dtype: float64

In [16]: np.exp(s)
Out[16]:
a    1.741168
b    1.165783
c    0.948482
d    1.166309
e    0.651514
dtype: float64

与NumPy数组一样,Series也有dtype属性,并且一般情况下其类型就是NumPy的dtype类型:

In [17]: s.dtype
Out[17]: dtype('float64')

In [18]: type(s.dtype)
Out[18]: numpy.dtype[float64]

如果需要获取Series底层的数组,可以用Series.array方法:

In [19]: s.array
Out[19]:
<PandasArray>
[  0.5545560665697387,  0.15339337717045246, -0.05289228660105809,
  0.15384372819622366,  -0.4284568761531815]
Length: 5, dtype: float64

当你需要在没有索引的情况下执行某些操作时(例如,禁用自动对齐),访问数组会很有用。

你也可以通过Series.to_numpy()方法获取对应的NumPy数组ndarray

In [20]: s.to_numpy()
Out[20]: array([ 0.55455607,  0.15339338, -0.05289229,  0.15384373, -0.42845688])

类dict

前面看到了可以从字典创建Series,很巧合的是(或者也是pamdas开发者别有用心的设计),原始的字典和通过该字典创建的Series在数据的获取和设置方面基本一样(除了字典可以添加新的key)。也就是说,Series类似于固定大小的dict,可以通过索引标签获取和设置值:

In [21]: s['a']
Out[21]: 0.5545560665697387

In [22]: s['e'] = 12.0

In [23]: s
Out[23]:
a     0.554556
b     0.153393
c    -0.052892
d     0.153844
e    12.000000
dtype: float64

# 注意,这里是查看e是否在s的索引中,而不是s的值
In [24]: 'e' in s
Out[24]: True

In [25]: 'f' in s
Out[25]: False

跟字典一样,如果没有相应的标签,也会抛出KeyError的异常:

In [25]: s['f']
KeyError: 'f'

使用get方法,获取Series中没有的标签的值将返回None或指定的默认值:

In [26]: s.get('f')

In [27]: s.get('f', np.nan)
Out[27]: nan

向量化操作与标签对齐

使用原始NumPy数组时,通常不需要逐个值循环。在pandas中使用Series时也是如此。Series也可以传递给大多数需要ndarray的NumPy方法。

In [28]: s + s
Out[28]:
a     1.109112
b     0.306787
c    -0.105785
d     0.307687
e    24.000000
dtype: float64

In [29]: s * 2
Out[29]:
a     1.109112
b     0.306787
c    -0.105785
d     0.307687
e    24.000000
dtype: float64

In [30]: np.square(s)
Out[30]:
a      0.307532
b      0.023530
c      0.002798
d      0.023668
e    144.000000
dtype: float64

Seriesndarray之间的一个关键区别是Series之间的运算会根据标签自动对齐数据,也就是运算是在具有相同标签的元素之间进行的。

In [31]: s[1:] + s[:-1]
Out[31]:
a         NaN
b    0.306787
c   -0.105785
d    0.307687
e         NaN
dtype: float64

未对齐Series之间的运算结果的索引是所有涉及到的Series的索引的并集。如果有标签在其中一个Series中没找到,那么在结果中该标签对应的值为NaN。无需进行任何明确的数据对齐即可编写代码,这为交互式数据分析和研究提供了极大的自由度和灵活性。pandas数据结构集成的数据对齐功能使pandas有别于其他大多数用于处理标签数据的相关工具。

通常,为了避免信息丢失,我们会选择具有不同索引的对象做计算之后的默认结果,这个结果的索引为参与计算的对象索引的并集,结果中会有某些标签的值被标记为确实数据(NaN),因为这些标签不是其中一个或多个对象中的标签。而这样的数据通常也是计算结果的重要信息。如果你不需要这些缺失数据,可以通过dropna函数将这些缺失数据极其标签丢弃掉。

名称属性

Series有一个name属性:

In [32]: s = pd.Series(np.random.randn(5), name='something')

In [32]: s
Out[32]:
0   -0.033113
1   -1.375810
2    1.026355
3    1.023575
4   -0.522689
Name: something, dtype: float64

In [33]: s.name
Out[33]: 'something'

在很多情况下,Seriesname属性可以被自动赋值,特别是当从DataFrame中选择单个列时,name将被赋值为对应的列标签。

可以用方法Series.rename()Series重命名,该方法会返回一个新的Series对象:

In [34]: s2 = s.rename('different')

In [35]: s2.name
Out[35]: 'different'

In [36]: s.name
Out[36]: 'something'

DataFrame

DataFrame是最常使用的pandas对象。它是一种二维的有标签的数据结构,其列可能具有不同的数据类型。你可以将其视为电子表格、SQL表或者Series对象的字典。

【pandas数据分析】pandas数据结构_第2张图片

创建DataFrame

创建DataFrame的基本方法如下:

df = pd.DataFrame(data, [index=index, columns=columns])

Series一样,DataFrame接受多种不同类型的输入data

  • 一维ndarray、列表、字典、Series等对象的字典

  • 二维NumPy ndarray

  • 结构化或记录ndarray

  • 一个Series

  • 另一个DataFrame

除了data之外,可选的,还可以传递index(行标签)和columns(列标签)参数。如果传递了索引和/或列参数,那么生成的DataFrame对象的索引和/或列就是你所指定的索引和/或列。如果没有传递轴标签,那么将根据默认规则从输入数据中构造出来。

从Series的字典创建

在这里,结果的索引是各个Series索引的并集。因此,可能存在索引的某些标签不在某些Series中,那么在生成的DataFrame中,这些标签对应的这些Series列的值会被设置为NaN

In [37]: d = {
    ...:     "one": pd.Series([1.0, 2.0, 3.0], index=["a", "b", "c"]),
    ...:     "two": pd.Series([1.0, 2.0, 3.0, 4.0], index=["a", "b", "c", "d"]),
    ...: }

# 没有传递索引和列,则结果的索引为各个Series索引的并集,列是字典的键
In [38]: df = pd.DataFrame(d)

In [39]: df
Out[39]:
   one  two
a  1.0  1.0
b  2.0  2.0
c  3.0  3.0
d  NaN  4.0
# 指定index,Series中匹配标签的数据会被取出,没有匹配的标签的值为NaN
In [40]: pd.DataFrame(d, index=["d", "b", "a"])
Out[40]:
   one  two
d  NaN  4.0
b  2.0  2.0
a  1.0  1.0

# 同时指定了索引和列,同样的,如果字典中没有和指定列标签匹配的键,则结果中该列标签对应的列值都为NaN
In [41]: pd.DataFrame(d, index=["d", "b", "a"], columns=["two", "three"])
Out[41]:
   two three
d  4.0   NaN
b  2.0   NaN
a  1.0   NaN

行标签和列标签(也是列名)分别可以通过indexcolumns属性访问:

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

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

从字典对象的字典创建

这里,这些嵌套字典会先被转换为Series,再从Series的字典创建DataFrame

In [44]: dd = {
    ...:     'one': {'a':1, 'b':2},
    ...:     'two': {'c':3, 'd':4}}

In [45]: dd
Out[45]: {'one': {'a': 1, 'b': 2}, 'two': {'c': 3, 'd': 4}}

In [46]: pd.DataFrame(dd)
Out[46]:
   one  two
a  1.0  NaN
b  2.0  NaN
c  NaN  3.0
d  NaN  4.0

从ndarray或列表的字典创建

ndarray或列表的长度必须相同。如果指定了索引,则索引的长度也必须和数组/列表的长度相同。如果没有传递索引,则会自动创建一个整数索引range(n),n为数组/列表的长度。

In [47]: d = {'one': np.array([1.0, 2.0, 3.0, 4.0]), 'two': np.array([4.0, 3.0, 2.0, 1.0])}

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

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

In [50]: d = {'one': [1.0, 2.0, 3.0, 4.0], 'two': [4.0, 3.0, 2.0, 1.0]}

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

从结构化或记录数组创建

这种情况的处理方式与数组的字典相同。

In [52]: data = np.array([(1, 2.0, "Hello"), (2, 3.0, "World")], dtype=[("A", "i4"), ("B", "
    ...: f4"), ("C", "a10")])

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

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

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

In [56]: pd.DataFrame(data, columns=["C", "A", "B"])
Out[56]:
          C  A    B
0  b'Hello'  1  2.0
1  b'World'  2  3.0

从字典列表创建

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

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

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

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

从元组字典创建

可以通过传递元组字典来自动创建有多级索引的DataFrame

In [61]: 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[61]:
       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

从一个Series创建

从一个Series创建的DataFrame只有一列数据,列的名称是Series的原始名称(在没有提供其他列名时),其索引与输入的Series相同。

In [62]: ser = pd.Series(range(3), index=list('abc'), name='ser')

In [63]: pd.DataFrame(ser)
Out[63]:
   ser
a    0
b    1
c    2

从命名元组列表创建

列表中第一个namedtuple的字段名决定了DataFrame的列。之后的命名元组(或元组)就只是简单地将值拆包并填充到DataFrame的行。如果这些元组中的任何一个的长度比第一个namedtuple短,则相应行中后面的列将标记为缺失值。但如果有任意一个比第一个namedtuple长,则会抛出ValueError

In [64]: from collections import namedtuple

In [65]: Point = namedtuple("Point", "x y")

# 列由第一个命名元组Point(0, 0)的字段决定,后续的元组可以是命名元组,也可以是普通元组
In [66]: pd.DataFrame([Point(0, 0), Point(0, 3), (2, 3)])
Out[66]:
   x  y
0  0  0
1  0  3
2  2  3

In [67]: Point3D = namedtuple("Point3D", "x y z")

# 第一个元组决定了生成的DataFrame由3列(x,y,z),而列表中第三个元组长度为2
# 所以在DataFrame的第三行中,第三列的值为NaN
In [68]: pd.DataFrame([Point3D(0, 0, 0), Point3D(0, 3, 5), Point(2, 3)])
Out[68]:
   x  y    z
0  0  0  0.0
1  0  3  5.0
2  2  3  NaN

从数据类列表中创建

DataFrame的构造函数传递数据类列表等价于传递字典列表,但是要注意的是,列表中的所有值都应该是数据类,在列表中混合类型会导致TypeError异常。

In [69]: from dataclasses import make_dataclass

In [70]: Point = make_dataclass('Point', [('x', int), ('y', int)])

In [71]: pd.DataFrame([Point(0, 0), Point(0, 3), Point(2, 3)])
Out[71]:
   x  y
0  0  0
1  0  3
2  2  3

其他创建方式

除了使用类构造函数创建DataFrame对象,DataFrame类本身也提供了一些类方法用于创建对象

DataFrame.from_dict

接受一个字典的字典或类数组序列的字典作为参数并放回一个DataFrame。它的行为类似于DataFrame构造函数,不同的是,它有一个orient参数,默认值为columns,也可以设置为index,从而将字典的key用作行标签。

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

# 传递index给orient参数,字典key会变成行标签
In [73]: pd.DataFrame.from_dict(
     ...:     dict([('A', [1, 2, 3]), ('B', [4, 5, 6])]),
     ...:     orient='index',
     ...:     columns=['one', 'two', 'three'],
     ...: )
Out[73]:
   one  two  three
A    1    2      3
B    4    5      6

DataFrame.from_records

接受一个元组或结构化数组的列表作为参数,其工作原理和普通的DataFrame构造函数类似,不同的是,生成的DataFrame的索引可以是结构化数据类型的特定字段。

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

# 指定字段C的数据作为index
In [75]: pd.DataFrame.from_records(data, index='C')
Out[75]:
          A    B
C
b'Hello'  1  2.0
b'World'  2  3.0

DataFrame操作

列的选择、添加和删除

Series一样,DataFrame也和字典类似,你可以在语义上将其视为Series对象的字典,这些Series对象共享相同的索引。获取、设置和删除列的语法与对应的字典操作相同:

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

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

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

In [78]: df
Out[78]:
   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

可以像字典操作那样将列删除或弹出:

In [79]: del df['two']

In [80]: three = df.pop('three')

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

In [82]: three
Out[82]:
a    1.0
b    4.0
c    9.0
d    NaN
Name: three, dtype: float64

插入标量值时,这个值会填充整个列:

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

当插入一个和DataFrame索引不同的Series时,只有匹配的标签的值会被保留,不在DataFrame索引中的标签值则被丢弃,Series索引中没有的标签值则设为NaN

In [84]: df['one_trunc'] = pd.Series([1,2,3,4], index=list('acef'))

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

也可以插入原始ndarray,但其长度必须与DataFrame索引的长度匹配。

In [86]: df['array'] = np.array([5, 6, 7, 8])

In [87]: df
Out[87]:
   one   flag  foo  one_trunc  array
a  1.0  False  bar        1.0      5
b  2.0  False  bar        NaN      6
c  3.0   True  bar        2.0      7
d  NaN  False  bar        NaN      8

默认情况下,列被插入到末尾,可以使用DataFrame.insert()方法在列中的特定位置插入:

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

In [89]: df
Out[89]:
   one  bar   flag  foo  one_trunc  array
a  1.0  1.0  False  bar        1.0      5
b  2.0  2.0  False  bar        NaN      6
c  3.0  3.0   True  bar        2.0      7
d  NaN  NaN  False  bar        NaN      8

在方法链中分配新列

DataFrame有一个assign()方法,可以轻松地创建从现有列派生的新列。

In [90]: iris = pd.DataFrame({
     ...:     'SepalLength': [5.1, 4.9, 4.7, 4.6, 5.0],
     ...:     'SepalWidth': [3.5, 3.0, 3.2, 3.1, 3.6],
     ...:     'PetalLength': [1.4, 1.4, 1.3, 1.5, 1.4],
     ...:     'PetalWidth': [0.2, 0.2, 0.2, 0.2, 0.2],
     ...:     'Name': 'Iris-setosa'
     ...: })

In [91]: iris
Out[91]:
   SepalLength  SepalWidth  PetalLength  PetalWidth         Name
0          5.1         3.5          1.4         0.2  Iris-setosa
1          4.9         3.0          1.4         0.2  Iris-setosa
2          4.7         3.2          1.3         0.2  Iris-setosa
3          4.6         3.1          1.5         0.2  Iris-setosa
4          5.0         3.6          1.4         0.2  Iris-setosa

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

也可以传递一个只接受一个参数的函数对象,在这个过程中,调用assign方法的DataFrame对象会被传递给这个函数,由这个函数产生新列:

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

assing()返回的是数据的副本并插入新列,原始DataFrame保持不变。

当你没有对DataFrame的引用时,传递可调用对象(而不是要插入的实际值)非常有用,在操作链中使用assign()时这是很常见的事情。例如下面的操作(过滤、计算新列、绘图):

In [94]: (
     ...:     iris.query("SepalLength > 5")
     ...:     .assign(
     ...:         SepalRatio=lambda x: x.SepalWidth / x.SepalLength,
     ...:         PetalRatio=lambda x: x.PetalWidth / x.PetalLength,
     ...:     )
     ...:     .plot(kind="scatter", x="SepalRatio", y="PetalRatio")
     ...: )
Out[94]: <AxesSubplot:xlabel='SepalRatio', ylabel='PetalRatio'>

assign()方法还有一个特性,它允许依赖赋值,在提供给assign方法的参数中,参数表达式是按顺序从左到右进行计算的,后面的表达式可以引用前面已经创建的列:

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

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

索引/选择

基本的索引方式如下:

操作 语法 结果
选择列 df[col]df.col Series
按标签选择行 df.loc[label] Series
按整数位置选择行 df.iloc[loc] Series
对行切片 df[5:10] DataFrame
按布尔向量选择行 df[bool_vec] DataFrame

可以看到选择列还有一个语法df.col,而且列名col必须为有效的Python变量名才可以用这种语法。

下面是一些示例:

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

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

# 按标签选择行
In [99]: df.loc['a']
Out[99]:
one            1.0
bar            1.0
flag         False
foo            bar
one_trunc      1.0
array            5
Name: a, dtype: object

# 按整数位置选择行
In [100]: df.iloc[0]
Out[100]:
one            1.0
bar            1.0
flag         False
foo            bar
one_trunc      1.0
array            5
Name: a, dtype: object

# 对行切片
In [101]: df[0:2]
Out[101]:
   one  bar   flag  foo  one_trunc  array
a  1.0  1.0  False  bar        1.0      5
b  2.0  2.0  False  bar        NaN      6

# 按布尔向量选择行
In [102]: df
Out[102]:
   one  bar   flag  foo  one_trunc  array
a  1.0  1.0  False  bar        1.0      5
b  2.0  2.0  False  bar        NaN      6
c  3.0  3.0   True  bar        2.0      7
d  NaN  NaN  False  bar        NaN      8

In [103]: df[df.flag]
Out[103]:
   one  bar  flag  foo  one_trunc  array
c  3.0  3.0  True  bar        2.0      7

数据对齐和算数操作

DataFrame对象之间的数据对齐在列和索引上自动对齐。与Series的数据对齐一样,生成的对象将具有列和行标签的并集。

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

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

In [106]: df + df2
Out[106]:
          A         B         C   D
0 -0.422414 -3.829047  0.155791 NaN
1  0.405465 -1.537037 -0.395309 NaN
2 -0.599319  0.584357 -2.368686 NaN
3 -0.931744 -1.190795 -2.078240 NaN
4  0.565281  1.239038 -0.777809 NaN
5  0.805762 -0.429504  1.629823 NaN
6  2.091268 -0.019281 -0.444887 NaN
7       NaN       NaN       NaN NaN
8       NaN       NaN       NaN NaN
9       NaN       NaN       NaN NaN

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

In [107]: df - df.iloc[0]
Out[107]:
          A         B         C         D
0  0.000000  0.000000  0.000000  0.000000
1 -1.411174  3.171864 -0.180068 -0.764811
2 -1.774370  4.247326 -1.126468 -0.328530
3 -1.239705  3.417047 -2.341824 -1.944903
4 -0.934275  4.544993 -0.259307 -1.441780
5 -1.152844  3.030318 -0.367320 -0.017702
6  1.249936  4.775528 -0.056807 -1.413703
7 -1.148255  3.154681  1.145846 -0.666049
8 -2.037174  5.424244  1.108562  1.573868
9 -2.774255  3.373770 -0.089912  0.600061

与标量的算术运算则是按元素操作:

In [108]: df * 5 + 2
Out[108]:
           A          B         C         D
0   7.703396 -18.667260  3.099865  1.053464
1   0.647527  -2.807940  2.199523 -2.770593
2  -1.168453   2.569370 -2.532477 -0.589185
3   1.504869  -1.582027 -8.609254 -8.671051
4   3.032019   4.057706  1.803329 -6.155438
5   1.939174  -3.515669  1.263263  0.964955
6  13.953077   5.210381  2.815829 -6.015049
7   1.962119  -2.893855  8.829094 -2.276782
8  -2.482472   8.453959  8.642675  8.922804
9  -6.167879  -1.798413  2.650303  4.053769

In [109]: 1 / df
Out[109]:
            A         B          C         D
0    0.876671 -0.241929   4.546014 -5.282420
1   -3.696930 -1.039946  25.059785 -1.048088
2   -1.578057  8.781640  -1.103150 -1.931110
3  -10.098344 -1.395858  -0.471287 -0.468557
4    4.844874  2.429891 -25.423112 -0.613088
5  -82.202122 -0.906508  -6.786678 -4.830706
6    0.418302  1.557448   6.128733 -0.623826
7 -131.993123 -1.021689   0.732162 -1.169103
8   -1.115456  0.774718   0.752709  0.722251
9   -0.612154 -1.316339   7.688727  2.434549

In [110]: df ** 4
Out[110]:
              A           B          C          D
0  1.692989e+00  291.911773   0.002341   0.001284
1  5.353465e-03    0.854980   0.000003   0.828723
2  1.612534e-01    0.000168   0.675247   0.071907
3  9.616108e-05    0.263412  20.270259  20.746686
4  1.814973e-03    0.028685   0.000002   7.077976
5  2.190120e-08    1.480856   0.000471   0.001836
6  3.266171e+01    0.169960   0.000709   6.603052
7  3.294540e-09    0.917750   3.479945   0.535289
8  6.459373e-01    2.776034   3.115243   3.674923
9  7.121266e+00    0.333065   0.000286   0.028466

布尔运算符也是按元素操作,对相同位置的元素做布尔运算:

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

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

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

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

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

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

转置

ndarray一样,要进行转置,访问T属性或者调用DataFrame.transpose()方法:

In [117]: df.T
Out[117]:
          0         1         2         3  ...         6         7         8         9
A  1.140679 -0.270495 -0.633691 -0.099026  ...  2.390615 -0.007576 -0.896494 -1.633576
B -4.133452 -0.961588  0.113874 -0.716405  ...  0.642076 -0.978771  1.290792 -0.759683
C  0.219973  0.039905 -0.906495 -2.121851  ...  0.163166  1.365819  1.328535  0.130061
D -0.189307 -0.954119 -0.517837 -2.134210  ... -1.603010 -0.855356  1.384561  0.410754

DataFrame与NumPy函数的互操作

大多数NumPy函数可以在SeriesDataFrame上直接调用

In [118]: np.abs(df)
Out[118]:
          A         B         C         D
0  1.140679  4.133452  0.219973  0.189307
1  0.270495  0.961588  0.039905  0.954119
2  0.633691  0.113874  0.906495  0.517837
3  0.099026  0.716405  2.121851  2.134210
4  0.206404  0.411541  0.039334  1.631088
5  0.012165  1.103134  0.147347  0.207009
6  2.390615  0.642076  0.163166  1.603010
7  0.007576  0.978771  1.365819  0.855356
8  0.896494  1.290792  1.328535  1.384561
9  1.633576  0.759683  0.130061  0.410754

In [119]: np.asarray(df)
Out[119]:
array([[ 1.14067923, -4.13345204,  0.21997292, -0.18930719],
       [-0.27049469, -0.961588  ,  0.03990457, -0.95411851],
       [-0.63369057,  0.11387395, -0.90649543, -0.51783703],
       [-0.09902614, -0.71640548, -2.12185072, -2.13421017],
       [ 0.20640373,  0.41154111, -0.03933429, -1.63108755],
       [-0.01216514, -1.10313382, -0.14734749, -0.20700908],
       [ 2.39061544,  0.64207621,  0.16316587, -1.60300982],
       [-0.00757615, -0.97877097,  1.36581882, -0.85535631],
       [-0.89649441,  1.29079181,  1.32853493,  1.38456089],
       [-1.63357585, -0.75968252,  0.13006054,  0.41075372]])

当传递两个pandas对象给NumPy函数时,会先进行对齐再执行函数操作:

In [120]: ser1 = pd.Series([1, 2, 3], index=['a', 'b', 'c'])

In [121]: ser2 = pd.Series([1, 3, 5], index=['b', 'a', 'c'])

In [122]: ser1
Out[122]:
a    1
b    2
c    3
dtype: int64

In [123]: ser2
Out[123]:
b    1
a    3
c    5
dtype: int64

In [124]: np.remainder(ser1, ser2)
Out[124]:
a    1
b    0
c    3
dtype: int64

Series一样,可以使用DataFrame.to_numpy()方法获得相应的ndarray

In [125]: df.to_numpy()
Out[125]:
array([[ 1.14067923, -4.13345204,  0.21997292, -0.18930719],
       [-0.27049469, -0.961588  ,  0.03990457, -0.95411851],
       [-0.63369057,  0.11387395, -0.90649543, -0.51783703],
       [-0.09902614, -0.71640548, -2.12185072, -2.13421017],
       [ 0.20640373,  0.41154111, -0.03933429, -1.63108755],
       [-0.01216514, -1.10313382, -0.14734749, -0.20700908],
       [ 2.39061544,  0.64207621,  0.16316587, -1.60300982],
       [-0.00757615, -0.97877097,  1.36581882, -0.85535631],
       [-0.89649441,  1.29079181,  1.32853493,  1.38456089],
       [-1.63357585, -0.75968252,  0.13006054,  0.41075372]])

你可能感兴趣的:(Python,#,pandas,pandas,数据结构,python,数据分析)