目录
2.2.3 由ndarray或者列表的字典创建DataFrame
2.2.10.6 DataFrame与NumPy函数的互操作
一、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 可以从各种文件格式比如
CSV
、JSON
、SQL
、MicrosoftExcel
导入数据。 -
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())