Bootstrap

python——pandas用法详解

目录

一、pandas简介

1.1 pandas来源

1.2 pandas特点

1.3 pandas的两种主要数据结构

二、 pandas数据结构详解

2.1 pandas——series

2.1.1 由字典创建一个series

2.1.2 由ndarray创建Series

2.1.3 由标量创建Series

2.1.4 Series特性

2.1.4.1 类ndarray

2.1.4.2 类dict(字典)

2.1.4.3 向量化操作(广播)和标签对齐

2.1.4.4 名称属性

2.2 DataFrame(二维数据)

2.2.1 由Series的字典创建DataFrame

2.2.2 由字典对象创建一个DataFrame

2.2.3 由ndarray或者列表的字典创建DataFrame

2.2.4 由结构化或者记录数创建数组

2.2.4 由字典列表创建DataFrame

2.2.5 由元组字典创建DataFrame

2.2.6 由一个Series创建DataFrame

2.2.7 由命名元组创建DataFrame

2.2.8 由数据类列表创建

2.2.9 其他创建方法

DataFrame.from_dict

DataFrame.from_records

2.2.10 DataFrame操作

2.2.10.1 列的选择、添加和删除

2.2.10.2 在方法链中分配新列

2.2.10.3 索引/选择

2.2.10.4 数据对齐和算术运算

2.2.10.5 装置

2.2.10.6 DataFrame与NumPy函数的互操作

三、pandas的基本操作

3.1 导入常用库

3.2 读写文件

3.3 索引

3.4 通过loc函数索引

3.5 数据的增删

3.6 数据的修改和查找

3.7 时间日期格式处理

3.8 数据堆叠和合并

3.9 字符串处理

3.10 数据统计和排序

3.11 读取txt文件


一、pandas简介

1.1 pandas来源

Python本身的数据分析功能并不强,需要安装一些第三方的扩展库增强它的能力。其中,针对结构化数据(可简单理解为二维表数据,或我们常用的Excel表格数据)分析能力最强的第三方扩展库就是Pandas

Pandas 是python的一个数据分析包,最初由AQR Capital Management于2008年4月开发,并于2009年底开源出来的,目前由专注于Python数据包开发的PyData开发team继续开发和维护,属于PyData项目的一部分。Pandas最初被作为金融数据分析工具而开发出来,因此,pandas为时间序列分析提供了很好的支持。 Pandas的名称来自于面板数据(panel data)和python数据分析(data analysis)。

1.2 pandas特点

  • Pandas基于NumPy 的一种工具包,是为解决数据分析任务而创建的。但Numpy只能处理数字,若想处理其他类型的数据,如字符串,就要用到Pandas了。

  • Pandas 纳入了大量库和一些标准的数据模型,提供了高效操作大型数据集所需的工具。

  • Pandas提供了大量能使我们快速便捷处理数据函数方法,是使Python成为强大而高效的数据分析语言的重要因素之一。

  • Pandas 可以从各种文件格式比如CSVJSONSQL、Microsoft Excel 导入数据。

  • Pandas 可以对各种数据进行运算操作,比如归并、再成形、选择,还有数据清洗数据加工特征。

  • Pandas 广泛应用在学术金融统计学等各个数据分析领域。

1.3 pandas的两种主要数据结构

Pandas 的主要数据结构是 Series (一维数据)与 DataFrame(二维数据),这两种数据结构足以处理金融、统计、社会科学、工程等领域里的大多数典型用例。

Series 是一种类似于一维数组的对象,它由一组数据(各种Numpy数据类型)以及一组与之相关的数据标签(即索引)组成。

 

DataFrame是一个表格型的数据结构,它含有一组有序的列,每列可以是不同的值类型(数值、字符串、布尔型值)。DataFrame 既有行索引也有列索引,它可以被看做由Series 组成的字典`(共同用一个索引)。

二、 pandas数据结构详解

 

2.1 pandas——series

创建Series的基本方法:

#s = pandas.Series(data, [index=index])

其中data可以是不同的数据类型,可以是字典,ndarray和标量,索引index是一个可选参数,最终生成的索引取决于传入的data是什么数据类型。

2.1.1 由字典创建一个series
data = {'b': 1, 'a': 0, 'c': 2}
ser1 = pd.Series(data)
print(ser1)
​
#结果
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中用于标记缺失数据)

示例:

data = {'b': 1, 'a': 0, 'c': 2}
index = ['b', 'c', 'd', 'a']
ser1 = pd.Series(data=data,index=index)
print(ser1)
​
结果:
b    1.0
c    2.0
d    NaN
a    0.0
dtype: float64

2.1.2 由ndarray创建Series

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

ser2 = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])
print(ser2)
print(ser2.values) #输出值
print(ser2.index)  #输出索引
ser2 = pd.Series(np.random.randn(5))
print(ser2)
​
#结果
a   -0.691551
b    0.484485
c    0.240262
d   -1.184450
e   -0.533851
dtype: float64
[-0.69155096  0.48448515  0.24026173 -1.18444977 -0.53385096]
Index(['a', 'b', 'c', 'd', 'e'], dtype='object')
0    1.118744
1    1.570550
2   -0.069444
3   -0.086134
4   -0.950202
dtype: float64

2.1.3 由标量创建Series

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

ser3 = pd.Series(5.0, index=['a', 'b', 'c', 'd', 'e'])
print(ser3)
​
#结果
a    5.0
b    5.0
c    5.0
d    5.0
e    5.0
dtype: float64

2.1.4 Series特性
2.1.4.1 类ndarray

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

ser2 = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])
print(ser2[0])
print(ser2[1:3])
print(ser2[ser2 > ser2.median()])  #大于中值
print(np.exp(ser2))

pandas也有dtype属性:

print(ser2.dtype)

获取Series底层的数组,可以用Series.array属性:

print(ser2.array)
​
#结果:
[  -0.7815805591394526, -0.033036078599971104,    1.4844533748085762,
    1.2854696909097223,   -0.9010676391265999]
Length: 5, dtype: float64

通过Series.to_numpy()方法获取对应的NumPy数组ndarray:

print(ser2.to_numpy())
​
#得到数组
[ 1.66084398 -0.07187783 -1.18355426  2.5447355  -0.45818444]

2.1.4.2 类dict(字典)

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

data = {'b': 1, 'a': 3, 'c': 2}
index = ['b', 'c', 'd', 'a']
ser1 = pd.Series(data=data,index=index)
# print(ser1)
​
print(ser1['a'])
ser1['f'] = 12
ser1['d'] = 10
print(ser1)
​
#判断是否有索引
print('f' in ser1)
print('z' in ser1)

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

print(ser1.get('z',np.nan))

2.1.4.3 向量化操作(广播)和标签对齐

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

示例:

data = {'b': 1, 'a': 3, 'c': 2}
index = ['b', 'c', 'd', 'a']
ser1 = pd.Series(data=data,index=index)
print(ser1 + ser1)
print(ser1 - 1)
print(np.square(ser1))

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

print(ser1[1:] + ser1[:-1])

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

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

print((ser1[1:] + ser1[:-1]).dropna())

2.1.4.4 名称属性

Series有一个name属性,可以理解为列名:

ser1 = pd.Series(data=data,index=index,name='test')
print(ser1.name)

可以用方法Series.rename()对Series重命名,该方法会返回一个新的Series对象,如果在原series上使用,原serise的名称属性不会改变:

ser1 = pd.Series(data=data,index=index,name='test')
print(ser1.name)
ser2 = ser1.rename('te')
print(ser2.name)

2.2 DataFrame(二维数据)

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

创建DataFrame的基本方法如下:

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

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

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

  • 二维NumPy ndarray

  • 结构化或记录ndarray

  • 一个`Series

  • 另一个DataFrame

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

2.2.1 由Series的字典创建DataFrame
#df = pd.DataFrame(data, [index=index, columns=columns])
​
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索引的并集,列是字典的键
df = pd.DataFrame(d)
print(df)
​
# 指定index,Series中匹配标签的数据会被取出,没有匹配的标签的值为NaN
df = pd.DataFrame(d, index=["d", "b", "a"])
print(df)
​
# 同时指定了索引和列,同样的,如果字典中没有和指定列标签匹配的键,则结果中该列标签对应的列值都为NaN
df = pd.DataFrame(d, index=["d", "b", "a"], columns=["two", "three"])
print(df)
​
#结果
   one  two
a  1.0  1.0
b  2.0  2.0
c  3.0  3.0
d  NaN  4.0
   one  two
d  NaN  4.0
b  2.0  2.0
a  1.0  1.0
   two three
d  4.0   NaN
b  2.0   NaN
a  1.0   NaN

2.2.2 由字典对象创建一个DataFrame

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

dd = {'one': {'a':1, 'b':2},
'two': {'c':3, 'd':4}}
​
pa = pd.DataFrame(dd)
print(pa)

外层字典的键名成为列索引,内层的键名成为行索引,无标签的默认值为Nan。

2.2.3 由ndarray或者列表的字典创建DataFrame

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

示例:

data = {'one': np.array([1.0, 2.0, 3.0, 4.0]), 'two': np.array([4.0, 3.0, 2.0, 1.0])}
pb = pd.DataFrame(data)
print(pb)
​
pc = pd.DataFrame(data, index=['a', 'b', 'c', 'd'])
print(pc)
​
data = {'one': [1.0, 2.0, 3.0, 4.0], 'two': [4.0, 3.0, 2.0, 1.0]}
pe = pd.DataFrame(data)
print(pe)
​
#结果:
   one  two
0  1.0  4.0
1  2.0  3.0
2  3.0  2.0
3  4.0  1.0
   one  two
a  1.0  4.0
b  2.0  3.0
c  3.0  2.0
d  4.0  1.0
   one  two
0  1.0  4.0
1  2.0  3.0
2  3.0  2.0
3  4.0  1.0

2.2.4 由结构化或者记录数创建数组

与数组相同:

data = np.array([(1, 2.0, "Hello"), (2, 3.0, "World")], dtype=[("A", "i4"), ("B", "f4"), ("C", "a10")])
pa = pd.DataFrame(data)
print(pa)
​
pb = pd.DataFrame(data, index=["first", "second"])
print(pb)
​
pc = pd.DataFrame(data, columns=["C", "A", "B"])
print(pc)
​
#结果:
   A    B         C
0  1  2.0  b'Hello'
1  2  3.0  b'World'
        A    B         C
first   1  2.0  b'Hello'
second  2  3.0  b'World'
          C  A    B
0  b'Hello'  1  2.0
1  b'World'  2  3.0
​
Process finished with exit code 0
 
2.2.4 由字典列表创建DataFrame

字典的键名默认为列名:

data2 = [{"a": 1, "b": 2}, {"a": 5, "b": 10, "c": 20}]
pa = pd.DataFrame(data2)
print(pa)
​
pb = pd.DataFrame(data2, index=["first", "second"])
print(pb)
​
pc = pd.DataFrame(data2, columns=["a", "b"])
print(pc)

2.2.5 由元组字典创建DataFrame

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

pa = 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}
     }
)
print(pa)
​
#结果:
       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

2.2.6 由一个Series创建DataFrame

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

ser = pd.Series(range(3), index=list('abc'), name='ser')
​
print(pd.DataFrame(ser))

2.2.7 由命名元组创建DataFrame

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

from collections import namedtuple
​
Point = namedtuple("Point", "x y")
​
# 列由第一个命名元组Point(0, 0)的字段决定,后续的元组可以是命名元组,也可以是普通元组
print(pd.DataFrame([Point(0, 0), Point(0, 3), (2, 3)]))
​
Point3D = namedtuple("Point3D", "x y z")
# 第一个元组决定了生成的DataFrame由3列(x,y,z),而列表中第三个元组长度为2
# 所以在DataFrame的第三行中,第三列的值为NaN
print(pd.DataFrame([Point3D(0, 0, 0), Point3D(0, 3, 5), Point(2, 3)]))

2.2.8 由数据类列表创建

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

from dataclasses import make_dataclass
Point = make_dataclass('Point', [('x', int), ('y', int)])
​
print(pd.DataFrame([Point(0, 0), Point(0, 3), Point(2, 3)]))

2.2.9 其他创建方法

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

DataFrame.from_dict

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

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

DataFrame.from_records

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

data = np.array([(1, 2., b'Hello'), (2, 3., b'World')],dtype=[('A', '<i4'), ('B', '<f4'), ('C', 'S10')])
print(pd.DataFrame.from_records(data, index='C'))# 指定字段C的数据作为index

2.2.10 DataFrame操作
2.2.10.1 列的选择、添加和删除

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

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索引的并集,列是字典的键
df = pd.DataFrame(d)
print(df)
print(df['one'])
df['three'] = df['one'] * df['two']
df['flag'] = df['one'] > 2 #布尔
print(df)

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

del df['one']
three = df.pop('three')
print(three)

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

df['foo'] = 'bar'
print(df)

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

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

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

df['array'] = np.array([5, 6, 7, 8])
print(df)

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

Dataframe.insert(loc, column, value, allow_duplicates=False): 在Dataframe的指定列中插入数据。

参数介绍:

   loc:  int型,表示第几列;若在第一列插入数据,则 loc=0
​
   column: 给插入的列取名,如 column='新的一列'
​
   value:数字,array,series等都可(可自己尝试)
​
   allow_duplicates: 是否允许列名重复,选择Ture表示允许新的列名与已存在的列名重复。
df.insert(1, 'one','bar') #在第二列插入
print(df)

2.2.10.2 在方法链中分配新列

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

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'
})
iris = iris.assign(sepal_ratio = iris['SepalWidth'] / iris['SepalLength'])
​
print(iris)

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

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'
})
# iris = iris.assign(sepal_ratio = iris['SepalWidth'] / iris['SepalLength'])
iris = iris.assign(sepal_ratio=lambda x: (x['SepalWidth'] / x['SepalLength']))
print(iris)

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

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

dfa = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
​
print(dfa.assign(C=lambda x: x['A'] + x['B'], D=lambda x: x['A'] + x['C']))

2.2.10.3 索引/选择

基本的索引方式如下:

 

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

data = {'one': np.array([1.0, 2.0, 3.0, 4.0]), 'two': np.array([4.0, 3.0, 2.0, 1.0])}
pb = pd.DataFrame(data)
# print(pb)
​
pc = pd.DataFrame(data, index=['a', 'b', 'c', 'd'])
print(pc)
​
#选择列
print(pc['one'])
print(pc.one)
​
#按照标签选择行
print(pc.loc['a'])
print(pc.iloc[2])
​
#对行切片
print(pc[:2])

还可以按照布尔值来取数据;

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索引的并集,列是字典的键
df = pd.DataFrame(d)
print(df)
print(df['one'])
df['three'] = df['one'] * df['two']
df['flag'] = df['one'] > 2
print(df)
​
#按照布尔型取值
print(df.flag)

2.2.10.4 数据对齐和算术运算

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

df = pd.DataFrame(np.random.randn(10, 4), columns=['A', 'B', 'C', 'D'])
print(df)
df2 = pd.DataFrame(np.random.randn(7, 3), columns=['A', 'B', 'C'])
print(df2)
print(df + df2)

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

print(df - df.iloc[0])

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

print(df * 5 - 2)
print(1 / df)
print(df ** 2)

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

df1 = pd.DataFrame({'a': [1, 0, 1], 'b': [0, 1, 1]}, dtype=bool)
df2 = pd.DataFrame({'a': [0, 1, 1], 'b': [1, 1, 0]}, dtype=bool)
print(df1 & df2)
print(df1 | df2)
print(df1 ^ df2)
print(-df1)

2.2.10.5 装置

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

print(df1.T)

2.2.10.6 DataFrame与NumPy函数的互操作

大多数NumPy函数可以在Series和DataFrame上直接调用:

df = pd.DataFrame(np.random.randn(10, 4), columns=['A', 'B', 'C', 'D'])
print(np.abs(df))
print(np.square(df))
print(np.asarray(df))

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

numpy.remainder()是另一个用于在numpy中进行数学运算的函数,它返回两个数组arr1和arr2之间的除法元素余数,即arr1 % arr2 当arr2为0且arr1和arr2都是整数数组时,返回0。

ser1 = pd.Series([1, 2, 3], index=['a', 'b', 'c'])
​
ser2 = pd.Series([1, 3, 5], index=['b', 'a', 'c'])
​
print(np.remainder(ser1, ser2))

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

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

三、pandas的基本操作

3.1 导入常用库

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
import  ssl
warnings.filterwarnings('ignore')
​
ssl._create_default_https_context = ssl._create_unverified_context

3.2 读写文件

读取CSV格式数据,使用 pd.read_csv(),写入文件使用DataFrame.to_csv():

work_path = '../save_file/'
os.chdir(work_path)#改变当前目录到work_path
print(os.getcwd())#打印当前目录
data = pd.read_csv(
    'data.csv', encoding = 'utf-8', dtype = {'id':str, 'name':str,'age':int})  #读取data.csv文件
print(data.head())
data.to_csv('data_out.csv', encoding = 'utf-8')# 读到data_out.csv,没有文件的话自动创建

3.3 索引

直接索引时,列更容易定位和索引,似乎行只能连续索引,不能灵活索引:

print(data.columns)#显示列标签
print(data['id'])#获取一列'user_id'
print(data[['id','name']])#获取两列,这是用列表形式获取的两列,所以有2层方括号
print(data[1:4])#获取1:3行,好像无法获取不连续的多行,需要用loc或iloc函数
print(data[['id','name']][1:4])#获取两列,再获取这两列组成的dataframe的1:4行

3.4 通过loc函数索引

loc函数定位的是标签(index),不是行的位置。loc函数的第一个参数是对行的操作,第二个参数是对列的操作。

print(data.loc[1:3, ['id','name']])#获取行标签为1:3,列标签为'id','name'的数据
print(data.loc[data.id == '1', ['id','name']])#获id列中值为'1'的行,列标签为'user_id','name'的数据
print(data.loc[data.age > 22, ])#age > 22的所有行
print(data.loc[(data.age > 24) | (data.id == '1') ])#age > 24或id='1'的所有行

iloc函数定位的是行的位置。iloc函数的第一个参数是对行的操作,第二个参数是对列的操作。

print(data.iloc[[1, 3], 0:2 ])#获取第1行和第4行,0到2列
print(data.iloc[[1, 3], [0, 2]])#获取第1行和第4行,0和2列

3.5 数据的增删

列的增加主要使用直接增加法,或者insert()方法,删除列主要使用drop、del方法。 行的删除也可以用drop方法,行的增加可以用_append在末尾加入,也有更好的方法。

tmp = data['age']
data.insert(0,'copy购买量',tmp)#在数据中插入一列,名称‘copy购买量’,位置第0列,数字tmp
print(data)
del data['copy购买量']#删除dataframe中名称为copy购买量的列
data.insert(1, 'copy购买量', tmp)#在数据中插入一列,名称‘copy购买量’,位置第0列,数字tmp
data.insert(0, 'copy购买量1', tmp)#在数据中插入一列,名称‘copy购买量’,位置第0列,数字tmp
data.drop(labels = ['copy购买量', 'copy购买量1'], axis = 1, inplace = True)
#上一行在数据中删除名称为'copy购买量', 'copy购买量1'的列,名称‘copy购买量’,axis = 1表示删除列,axis = 0表示删除行,inplace表示真实删除
print(data.drop(labels = [0, 2], axis = 0, inplace = False))#删除行标签为0和2的行
print(data.drop(labels = range(1, 3), axis = 0, inplace = False))#删除行标签1到2的行
print(data._append(data))#在df的最后附加行

3.6 数据的修改和查找

用rename修改行标签或者列名称。 直接用df.series同==,>,<等逻辑运算符获取满足特定关系的df数据 也可以用between、isin方法获取在之间,匹配某些数量的数据

data.rename(columns = {'id':'用户ID'},inplace=True)#用rename修改列标签,使用字典的形式;
data.rename(index = {1:11, 2:22}, inplace = True)#用rename修改行标签,使用字典的形式;
print(data)
data.loc[data['性别'] == 0, '性别'] = '女性'#将列标签为‘性别’的列中值等于0的元素,改为女性。
data.loc[data['性别'] == 1, '性别'] = '男性'
data.loc[data['性别'] == 2, '性别'] = '未知'
print(data)
print(data[data['age']>23])#获取名称为'age'列的数据中大于80的数据,会得到所有列
print(data[(data['age'] < 23) & (data['性别'] == '男性')])
print(data[data['age'].isin([12,21,20])])#age 列数值属于list[12,21,20]的数据

3.7 时间日期格式处理

使用df.to_datetime()处理pandas的各类字符串形式的时间; 使用pd.dt.strftime()处理datetime64或时间戳类型数据的以格式化字符串输出

start_datetime = np.arange('2021-11-01', '2021-11-10', dtype = 'datetime64[D]')#生成numpy起始时间序列
end_datetime = np.arange('2021-11-01T12:14:30.789', '2021-11-10T12:14:30.789', 86400000, dtype = 'datetime64[ms]')
segment_time = np.array([start_datetime, end_datetime]).T#建立一个起始时间的numpy数组
df = pd.DataFrame(segment_time, columns = ['起始时间', '结束时间'])#新建dataframe
df['持续时间'] = df['结束时间'] - df['起始时间']#增加一列持续的时间
df['间隔时间'] = np.append((df['起始时间'].values[1:] - df['结束时间'].values[0:-1]), 0)#增加一列每两组之间间隔的时间
df['start_time'] = df['起始时间'].dt.strftime('%A %B %d %Y %H:%M:%S.%f')#把datetime64格式转换成规定格式字符串时间
df['end_time'] = df['结束时间'].dt.strftime('%a %b %d %Y %H:%M:%S.%f')
df['读入起始时间'] = pd.to_datetime(df['start_time'], format = '%A %B %d %Y %H:%M:%S.%f')
df['读入结束时间'] = pd.to_datetime(df["end_time"], format = '%a %b %d %Y %H:%M:%S.%f')
print(df)
#建立df的另外一种方法:
start_datetime_char = np.datetime_as_string(start_datetime, unit = 'ms')#numpy里的datetime64转换为字符串,改格式不方便
end_datetime_char = np.datetime_as_string(end_datetime, unit = 'ms')#转换为字符串
df_char = pd.DataFrame(start_datetime_char, columns = ['起始时间'])
df_char['结束时间'] = end_datetime_char
print(df_char)

对datetime64使用pd.dt.date/time/year/month/wedk/weekday/day/hour/min/second 对timedelta64使用pd.dt.days/total_seconds() 对datetime64使用pd.dt.dayofyear/weekofyear

df_char['date_time'] = pd.to_datetime(df['结束时间'], format = '%Y-%m-%dT%H%M%S.%f')
df_char['年'] = df_char['date_time'].dt.year
df_char['周'] = df_char['date_time'].dt.isocalendar().week
df_char['周几'] = df_char['date_time'].dt.weekday
df_char['分'] = df_char['date_time'].dt.minute
df_char['秒'] = df_char['date_time'].dt.second
df_char['微秒'] = df_char['date_time'].dt.microsecond
df_char['总秒数'] = df['间隔时间'].dt.total_seconds()#不要忘了括号
df_char['总天数'] = (df['间隔时间']).dt.days#不用括号
df_char['加天序列后总天数'] = (df['间隔时间'] + np.arange(0, 9, 1, dtype = 'timedelta64[D]')).dt.days#不用括号
#上面的增加np.arange()主要是用来增加间隔天数,以使显示的天数由变化。
pd.set_option('display.max_columns', None)#设置pandas显示选项,以便显示所有列。
print(df_char)

3.8 数据堆叠和合并

使用concat()将两个dataframe横向堆叠或纵向堆叠 使用merge()将两个dataframe按照主键合并

merge1 = pd.concat([df, df_char], axis = 0, join = 'outer')#使用列表堆叠,0轴沿着纵向拓展,即行数增加,inner表示不一致的删除,outer表示不一致的保留
merge2 = pd.concat([df, df_char, data], axis = 1, join = 'outer')#使用列表堆叠,0轴沿着横向拓展,即列数增加
merge3 = pd.merge(left = df, right = df_char, how = 'inner', left_on = '结束时间', right_on = 'date_time')#按照df[结束时间]和df[date_time]这两个主键关联
merge4 = pd.merge(left = df, right = df_char, how = 'inner', left_on = '起始时间', right_on = 'date_time')#按照df[起始时间]和df[date_time]这两个主键关联,由于不相等且是inner模式,所以会清空所有数据
merge5 = pd.merge(left = df, right = df_char, how = 'outer', left_on = '起始时间', right_on = 'date_time')#按照df[起始时间]和df[date_time]这两个主键关联,由于不相等但是是outer模式,所以会保留所有数据
​
print(merge1)
print(merge2)
print(merge3)
print(merge4)
print(merge5)

3.9 字符串处理

主要用到的函数名称即说明如下: pd.str. contains() 返回表示各str是否含有指定模式的字符串 。 replace() 替换字符串 lower() 返回字符串的副本,其中所有字母都转换为小写。 upper() 返回字符串的副本,其中所有字母都转换为大写。 split() 返回字符串中的单词列表。 strip() 删除前导和后置空格。 join() 返回一个字符串,该字符串是给定序列中所有字符串的连接。 还有一个判断是否包含空元素的函数pd.isnull(),经常会需要和T、any()联合起来使用 ∙ \bull ∙使用df.fillna/df.age.fillna来替换缺失值

df1 = pd.read_csv('MotorcycleData.csv', encoding = 'gbk')
print(df1['Price'].str[0])#pd.str可以用切片方式实现索引
print(df1['Price'].str[:2])
df1['价格'] = df1['Price'].str.strip('$')#删去头尾的'$'符号
df1['价格'] = df1['价格'].str.replace(',', '')#删去','符号
df1['价格'] = df1['价格'].astype(int)
# print(df1.info())
# print(df1[['Price', '价格']])
df1['位置'] = df1['Location'].str.split(',')#通过字符串中“,”将其分割成一个字符串列表
# print(df1['位置'].str[1])#打印出这个列表中的第一个字符串元素
# print(df1['Location'].str.len())#获取字符串长度
df1.loc[df1[['Location']].isnull().T.any()] = 'aaa'#将'Location'这列中是空数据的填上字符串,避免字符串判断时出现空值而报错
#上面的例子中df1[['Location']]必须用两层中括号,这样结果是DataFrame类,如果是df1['Location']就是series类了
#非转置:df1.isnull().any(),得到的每一列求any()计算的结果,输出为列的Series。
#转置:df1.isnull().T.any(),得到的每一行求any()计算的结果,输出为行的Series。
#这里要知道那一行有Nan,所以要用转置
print(df1[df1[['Location', '价格']].isnull().T.any()]['Location'])#检查'Location和价格'这两列中是是否有空数据 
#替换缺失值用df.fillna更方便
df1.fillna('datamiss', inplace = True)
print(df1.loc[df1['Location'].str.contains('New Hampshire'), 'Location'])#找出数据中包含特定字符串的数据

3.10 数据统计和排序

数据统计信息显示采用discribe()方法; 数据排序使用: ∙ \bullet ∙ sort_values(by, axis=0, ascending=True, inplace=False, kind=‘quicksort’, na_position=‘last’),基于某几行或列的值进行排列 ∙ \bullet ∙ sort_index(axis=0, level=None, ascending=True, inplace=False, kind=‘quicksort’, na_position=‘last’, sort_remaining=True, by=None),基于行或列标签进行排列

print(df1.describe())#只对数字类参数有用,输出每一列的均值、标准差、最大、最小值和25%/50%/75%的值
print(df1.sort_index(axis = 1))
print(df1.sort_values(by = ['Bid_Count', 'Price']))#根据指定的列名称及其优先级顺序,对整个列表进行排序。

3.11 读取txt文件

读取txt文件可以用read_csv也可以用read_table,前置默认以逗号分隔,后置必须指定分隔符。前者也可以用sep参数来指定分隔符。

数据排序使用:

dtxt = pd.read_table('sample_data_out.txt', encoding = 'gbk', header = 0, sep = ' ')
dtxt = pd.read_csv('sample_data_out.txt', encoding = 'gbk', header = 0, sep = ' ')
'''
上面的两句功能基本相同
'''
print(dtxt.iloc[:,0].str.split(','))#手动分隔,结果是两层列表
print(dtxt.columns.str.split(',')[0])#手动分隔列名称,结果是两层列表
dtxt_deal = (dtxt.iloc[:,0].str.split(',')).apply(pd.Series, index = dtxt.columns.str.split(',')[0])
dtxt_deal['buy_mount'] = dtxt_deal['buy_mount'].astype(int)
print(dtxt_deal.info())

;