pandas 最有趣的地方在于里面隐藏了很多包。它是一个核心包,里面有很多其他包的功能。这点很棒,因为你只需要使用 pandas 就可以完成工作。pandas 相当于 python 中 excel:它使用表(也就是 dataframe),能在数据上做各种变换,但还有其他很多功能。
操作 | 代码实现 | 返回值 | 说明 |
---|---|---|---|
通过ndarray构建DataFrame | pd.DataFrame(array) pd.DataFrame(np.random.randn(3,4), columns = [‘a’, ‘b’, ‘c’]) |
新DataFrame | 通过Numpy的ndarray构建, 自动生成行列索引, columns 可指定列索引名 |
通过dict构建DataFrame | pd.DataFrame(dict) dict = {‘A’: 1, ‘B’: pd.Timestamp(‘20190616’), ‘C’:pd.Series(1,index=list(range(4)),dtype=‘float32’), ‘D’: np.array([3] * 4,dtype=‘int32’), ‘E’: [“Python”,“Java”,“C++”,“C”], ‘F’: ‘tiger’ } |
新DataFrame | dict的key为列标签 value为元素 自动生成行索引 |
常用读取文件函数 read_csv, read_excel,read_clipboard, read_sql
如:data = pd.read_csv( my_file.csv , sep= ; , encoding= latin-1 , nrows=1000, skiprows=[2,5])
注:sep 代表的是分隔符。如果你在使用法语数据,excel 中 csv 分隔符是「;」,因此你需要显式地指定它。编码设置为 latin-1 来读取法语字符。nrows=1000 表示读取前 1000 行数据。skiprows=[2,5] 表示你在读取文件的时候会移除第 2 行和第 5 行。
data.to_csv( my_new_file.csv , index=None)
注:index=None 表示将会以数据本来的样子写入。如果没有写 index=None,你会多出一个第一列,内容是 1,2,3,…,一直到最后一行。
其他的写入函数.to_excel, .to_json, .to_pickle
Gives (#rows, #columns) 给出行数和列数
data.describe() 计算基本的统计数据
data.head() 默认返回5行 返回值为DataFrame
head( n ) 方法用于读取前面的 n 行,如果不填参数 n ,默认返回 5 行
data.tail() 默认返回5行 返回值为DataFrame
tail( n ) 方法用于读取尾部的 n 行,如果不填参数 n ,默认返回 5 行,空行各个字段的值返回 NaN。
data.shape 获取数据形状 返回值为元组
假设data为10行8列的数据 则返回(10,8)
扩展:在机器学习与深度学习模型中,需要将数据集划分为训练集和测试集会造成索引的混乱
可以通过shape方法让索引重新有序
如: data.index = range(data.shape[0])
也可以使用data.reset_index(drop=True)
data.T 进行数据转置 返回值为DataFrame 不改变原来的DataFrame
假设data为10行8列的数据 则返回8行10列的数据
data.index = list 整体全部修改index 返回值为DataFrame 不改变原来的DataFrame
说明:按照列表list的数据内容修改index,必须整体全部修改
错误示例:data.index[3] = ‘学生_3’
data.reset_index(drop=True) 重新设新的下标索引 返回值为DataFrame 不改变原来的DataFrame
说明:drop:默认为False,不删除原来索引,如果为True,删除原来的索引值
一般适用于索引发生混乱的时候,给索引重新从0编号
data.set_index(keys, drop=True) 把某列值设置为新的索引 返回值为DataFrame 不改变原来的DataFrame
说明:keys : 列索引名成或者列索引名称的列表 drop: 默认为False,不删除原来索引,如果为True,删除原来的索引值
data.info() 返回DataFrame的一些基本信息
操作 | 代码实现 | 返回值 | 说明 |
---|---|---|---|
删除若干列 | df.drop([“a”,“b”,“c”], axis=1) | 新DataFrame | 不改变原来的DataFrame |
删除列数据 | del(df[‘G’]) | None | 改变原来的DataFrame |
增加列数据 | df[‘G’]=list | DataFrame | 改变原来的DataFrame,list的长度必须的保持一致 |
对某一列重新赋值 | df[‘G’]=100 | DataFrame | 改变原来的DataFrame |
对值排序 | df.sort_values(by=“open”, ascending=False) df.sort_values(by=[‘open’, ‘high’]) |
DataFrame | 改变原来的DataFrame by:指定排序参考的键,单个键或者多个键进行排序 ascending:默认True升序,False降序 |
对某一列重新赋值 | df[‘G’]=100 | DataFrame | 改变原来的DataFrame |
对索引排序 | df.sort_index() | DataFrame | 改变原来的DataFrame ascending:默认True升序,False降序 |
loc函数:通过行索引 “Index” 中的具体值来取行数据(如取"Index"为"A"的行)
iloc函数:通过行号来取行数据(如取第二行的数据)
本章给出loc、iloc常见的五种用法,并附上详细代码。
1. 利用loc、iloc提取行数据
import numpy as np
import pandas as pd
#创建一个Dataframe
data=pd.DataFrame(np.arange(16).reshape(4,4),index=list('abcd'),columns=list('ABCD'))
In[1]: data
Out[1]:
A B C D
a 0 1 2 3
b 4 5 6 7
c 8 9 10 11
d 12 13 14 15
#取索引为'a'的行
In[2]: data.loc['a']
Out[2]:
A 0
B 1
C 2
D 3
#取第一行数据,索引为'a'的行就是第一行,所以结果相同
In[3]: data.iloc[0]
Out[3]:
A 0
B 1
C 2
D 3
2. 利用loc、iloc提取列数据
In[4]:data.loc[:,['A']] #取'A'列所有行,跨列取格式为 data.loc[:,['A','C']]意为取A列和C列的所有行
Out[4]:
A
a 0
b 4
c 8
d 12
In[5]:data.iloc[:,'A':'C'] #取连续的列可以用切片,取A~C所有行,注意是左闭右闭
Out[5]:
A B C
a 0 1 2
b 4 5 6
c 8 9 10
d 12 13 14
In[6]:data.iloc[:,[0]] #取第0列所有行,跨列取格式为 data.iloc[:,[0,2]]意为取第0列和第2列的所有行
Out[6]:
A
a 0
b 4
c 8
d 12
In[7]:data.iloc[:,0:2] #取连续的列可以用切片,取0~1的所有行,注意是左闭右开
Out[7]:
A B
a 0 1
b 4 5
c 8 9
d 12 13
3.利用loc、iloc提取指定行、指定列数据
In[8]:data.loc[['a','b'],['A','B']] #提取index为'a','b',列名为'A','B'中的数据
Out[8]:
A B
a 0 1
b 4 5
In[9]:data.loc['b':'d','A':'C'] #提取连续数据index为'b'-'d',列名为'A'-'C'中的数据 切片格式左闭右闭
Out[9]:
A B C
b 4 5 6
c 8 9 10
d 12 13 14
In[9]:data.iloc[[0,1],[0,1]] #提取第0、1行,第0、1列中的数据
Out[9]:
A B
a 0 1
b 4 5
In[10]:data.iloc[1:4,0:3] #提取第1-3行,第0-2列中的数据 切片格式左闭右开
Out[10]:
A B C
b 4 5 6
c 8 9 10
d 12 13 14
4.利用loc、iloc提取所有数据
In[11]:data.loc[:,:] #取A,B,C,D列的所有行
Out[11]:
A B C D
a 0 1 2 3
b 4 5 6 7
c 8 9 10 11
d 12 13 14 15
In[12]:data.iloc[:,:] #取第0,1,2,3列的所有行
Out[12]:
A B C D
a 0 1 2 3
b 4 5 6 7
c 8 9 10 11
d 12 13 14 15
5.利用loc函数,根据某个数据来提取数据所在的行
In[13]: data.loc[data['A']==0] #提取data数据(筛选条件: A列中数字为0所在的行数据)
Out[13]:
A B C D
a 0 1 2 3
In[14]: data.loc[(data['A']==0)&(data['B']==2)] #提取data数据(多个筛选条件)
Out[14]:
A B C D
a 0 1 2 3
同时,以下几种写法也可提取数据所在的行,与第五种用法类似,仅作补充。
In[15]: data[data['A']==0] #dataframe用法
In[16]: data[data['A'].isin([0])] #isin函数
In[17]: data[(data['A']==0)&(data['B']==2)] #dataframe用法
In[18]: data[(data['A'].isin([0]))&(data['B'].isin([2]))] #isin函数
Out[15]:
A B C D
操作 | 代码实现 | 返回值 | 说明 |
---|---|---|---|
算术运算 | df[‘open’].add(100) df[‘open’].sub(100) |
新Series | 不改变原来的DataFrame,实际上是Series运算 add(100)是该列每一行的元素都+100 sub(100)是该列每一列的元素都-100 |
逻辑运算 | df[“open”] > 100 | 新Series(bool类型) | 不改变原来的DataFrame,返回值是True与Flase的Serise |
筛选 | df[df[“open”] > 23] df[(df[“open”] > 23) & (df[“open”] < 24)] |
新DataFrame | 逻辑判断的结果可以作为筛选的依据 |
筛选函数query() | df.query(“open<24 & open>23”) | 新DataFrame | 筛选出符合条件的所有数据 |
筛选函数isin() | df[df[“open”].isin([23.53, 23.85])] | 新DataFrame | 筛选出"open"列中值为23.53和23.85的所有数据 |
有时候我们获取到数据之后,想要查看下数据的简单统计指标(最大值、最小值、平均值、中位数等),比如想要查看年龄的最大值,如何实现呢?直接对 age
这一列调用 max
方法即可。
user_info.age.max()
类似的,通过调用 min
、mean
、quantile
、sum
方法可以实现最小值、平均值、中位数以及求和
来介绍个有意思的方法:cumsum
,看名字就发现它和 sum
方法有关系,事实上确实如此,cumsum
也是用来求和的,不过它是用来累加求和的,也就是说它得到的结果与原始的 DataFrame 大小相同。cummax
最后的结果就是将上一次求和的结果与原始当前值求和作为当前值。cumsum
也可以用来操作字符串类型的对象。
data.describe()
返回结果如图
如果想要查看非数字类型的列的统计指标,可以设置 include=["object"]
来获得。
count:总数
unique:去重后的个数
top:出现频率最高的数
freq:出现的最高频率次数
user_info.describe(include=["object"])
调用 value_counts
方法快速获取 DataFrame 中每个值出现的次数。
user_info.sex.value_counts()
可以使用idxmax
或 idxmin
方法完成
user_info.age.idxmax()
apply(func, axis=0)
>>>data[['open', 'close']].apply(lambda x: x.max() - x.min(), axis=0) # 返回Series
===运行结果:======================================
open 22.74
close 22.85
dtype: float64
data[data[ column_1 ]== french ]
data[(data[ column_1 ]== french ) & (data[ year_born ]1990)]
data[(data[ column_1 ] french ) & (data[ year_born ]1990) & ~(data[ city ] London )]
通过逻辑运算来取数据子集。要使用 & (AND)、 ~ (NOT) 和 | (OR),必须在逻辑运算前后加上「and」。
data[data[ column_1 ].isin([ french , english ])]
除了可以在同一列使用多个 OR,你还可以使用.isin() 函数。
在Python中如果想要对数据使用函数,可以借助apply(),applymap(),map() 来应用函数,括号里面可以是直接函数式,或者自定义函数(def)或者匿名函数(lambad)
import pandas as pd
import numpy as np
from pandas import DataFrame
from pandas import Series
df1= DataFrame({
"sales1":[-1,2,3],
"sales2":[3,-5,7],
})
df1
#结果
sales1 sales2
0 -1 3
1 2 -5
2 3 7
1、当我们要对数据框(DataFrame)的数据进行按行或按列操作时用apply()
df1.apply(lambda x :x.max()-x.min(),axis=1)
#axis=1,表示跨列对数据进行操作 表格是3*2 axis=1 去掉维度2 得到维度3
#从下面的结果可以看出,我们使用了apply函数之后,系统自动按列寻找每一行的最大值和最小值计算,每一行输出一个值
#结果
0 4
1 7
2 4
dtype: int64
df1.apply(lambda x :x.max()-x.min(),axis=0)
#默认参数axis=0,表示跨行对数据进行操作 表格是3*2 axis=1 去掉维度3 得到维度2
#从下面的结果可以看出,我们使用了apply函数之后,系统自动按行寻找每一列的最大值和最小值计算,每一列输出一个值
#结果
sales1 4
sales2 12
dtype: int64
关于axis参数的理解,请见我另一篇博客
2、当我们要对数据框(DataFrame)的每一个数据进行操作时用applymap(),返回结果是DataFrame格式
df1.applymap(lambda x : 1 if x>0 else 0)
#从下面的结果可以看出,我们使用了applymap函数之后,
#系统自动对每一个数据进行判断,判断之后输出结果
#结果
sales1 sales2
0 0 1
1 1 0
2 1 1
3、当我们要对Series的每一个数据进行操作时用map()
df1["sales1"].map(lambda x : 1 if x>0 else 0)
#df1.sales1就是一个Series
#结果
0 0
1 1
2 1
Name: sales1, dtype: int64
补充:在处理大规模数据集时,pandas 会花费一些时间来进行.map()、.apply()、.applymap() 等操作。tqdm 是一个可以用来帮助预测这些操作的执行何时完成的包(是的,我说谎了,我之前说我们只会使用到 pandas)。
from tqdm import tqdm_notebook
tqdm_notebook().pandas()
用 pandas 设置 tqdm
data[ column_1 ].progress_map(lambda x: x.count( e ))
用 .progress_map() 代替.map()、.apply() 和.applymap() 也是类似的。在 Jupyter 中使用 tqdm 和 pandas 得到的进度条
tqdm会有另外的专题详细介绍,还没更新。
数据如下:
left = pd.DataFrame({'key1': ['K0', 'K0', 'K1', 'K2'],
'key2': ['K0', 'K1', 'K0', 'K1'],
'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3']})
right = pd.DataFrame({'key1': ['K0', 'K1', 'K1', 'K2'],
'key2': ['K0', 'K0', 'K0', 'K0'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']})
pd.concat([data1, data2], axis=1)
axis=0 跨行操作,行变列不变
axis=1 跨列操作,列变行不变
pd.concat([left,right],axis=1)
# 结果
key1 key2 A B key1 key2 C D
0 K0 K0 A0 B0 K0 K0 C0 D0
1 K0 K1 A1 B1 K1 K0 C1 D1
2 K1 K0 A2 B2 K1 K0 C2 D2
3 K2 K1 A3 B3 K2 K0 C3 D3
pd.concat([left,right],axis=0)
# 结果
key1 key2 A B C D
0 K0 K0 A0 B0 NaN NaN
1 K0 K1 A1 B1 NaN NaN
2 K1 K0 A2 B2 NaN NaN
3 K2 K1 A3 B3 NaN NaN
0 K0 K0 NaN NaN C0 D0
1 K1 K0 NaN NaN C1 D1
2 K1 K0 NaN NaN C2 D2
3 K2 K0 NaN NaN C3 D3
pd.merge(left, right, how='inner', on=None)
可以指定按照两组数据的共同键值对合并或者左右各自
left: DataFrame
right: 另一个DataFrame
on: 指定的共同键
how:按照什么方式连接
两个表中所有记录都保留;两个表不存在的数据,使用np.NaN值填充。
以左表为主,根据条件查询右表数据;左表数据全部保留,右表不存在的数据,使用np.NaN值填充。
以右表为主,根据条件查询左表数据;右表数据全部保留,左表不存在的数据,使用np.NaN值填充
DataFrame.groupby(key, as_index=False)
key:分组的列数据,可以多个
案例:不同颜色的不同笔的价格数据
进行分组,对颜色分组,对价格进行聚合
>>>col =pd.DataFrame({'color': ['white','red','green','red','green'],
'object': ['pen','pencil','pencil','ashtray','pen'],
'price1':[5.56,4.20,1.30,0.56,2.75],
'price2':[4.75,4.12,1.60,0.75,3.15]})
color object price1 price2
0 white pen 5.56 4.75
1 red pencil 4.20 4.12
2 green pencil 1.30 1.60
3 red ashtray 0.56 0.75
4 green pen 2.75 3.15
颜色分组 价格聚合
>>>col.groupby(['color'])['price1'].mean()
color
green 2.025
red 2.380
white 5.560
Name: price1, dtype: float64
>>>col['price1'].groupby(col['color']).mean()
color
green 2.025
red 2.380
white 5.560
Name: price1, dtype: float64
添加参数as_index=False后,注意数据的结构的变化
>>>col.groupby(['color'], as_index=False)['price1'].mean()
color price1
0 green 2.025
1 red 2.380
2 white 5.560
agg()---- aggregation聚合函数 (没怎么用过)
dataset.groupby('color').agg([list])
交叉表:用于计算一列数据对于另外一列数据的分组个数(用于统计分组频率的特殊透视表)
pd.crosstab(df["A"], df["B"])
透视表:透视表是将原有的DataFrame的列分别作为行索引和列索引,然后对指定的列应用聚集函数
dataframe.pivot_table([], index=[])
dataframe['列名'].unique()
方法:
DataFrame.duplicated(subset = None,keep =‘first’ )返回boolean数组 一个bool值代表一行
参数:
subset:用来指定特定的列,默认所有列
keep:{‘first’,‘last’,False},默认’first’
first:标记重复,True除了第一次出现。
last:标记重复,True除了最后一次出现。
False:将所有重复项标记为True。
import pandas as pd
if __name__=="__main__":
path = "./test.csv"
# path_other = "./test_.csv"
df = pd.read_csv(path, header=0, names=["DEVICE_ID","LNG", "LAT","TEN_GROUP","WEEKDAY","FLOW"])
# df.to_csv(path, mode="a", index=False, header=False)
print("DEVICE_ID列不同值数目:\n%s\n\n" % str(len(df['DEVICE_ID'].unique())))
#keep=False->重复的均置Ture
t=df.duplicated(subset=["DEVICE_ID"],keep=False)
print(t)
#False的个数即代表不重复的键的个数
print(len(t[t==False]))
#选取不重复的键
print("不重复键:")
print(df[~t])
#运行结果
DEVICE_ID列不同值数目:3
0 True
1 True
2 True
3 True
4 True
5 True
6 True
7 True
8 True
9 True
10 True
11 True
12 True
13 False
dtype: bool
1
不重复键:
DEVICE_ID LNG LAT TEN_GROUP WEEKDAY FLOW
13 3 123.0 43.0 102 1 29
df["DEVICE_ID"].value_counts()
数据初始化
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.randint(10, size=(1000000, 1)), columns=['num'])
(不建议使用)
for i in range(len(df)):
if df.iloc[i]['num'] < 5:
df.loc[i , 'num'] = 15
原理是将Dataframe迭代为Series,再返回结果。这一过程中需要进行类型检查,所以,会花费很长的时间。(不建议使用)
for index, row in df.iterrows():
if row['num'] < 5:
df.loc[index , 'num'] = 15
原理是将Dataframe迭代为tuple,再进行返回,由于元组不可变的特性,此过程不需要进行类型检查。(效率高,推荐使用)
for row in student.itertuples():
# print(row)
print(row.Index, row.name, row.account, row.pwd)
print(row.Index, getattr(row,'name'), getattr(row,'account'), getattr(row,'pwd'))
这种方法是直接手动构造原生tuple,无需关心index数据。(效率高,推荐使用)
for A, B in zip(df['A'], df['B']):
print(A, B)
这段时间一直在用pandas,今天运行前人代码发现报了一个warning:
SettingWithCopyWarning: A value is trying to be set on a copy of a
slice from a DataFrame See the caveats in the documentation:
http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
SettingWithCopyWarning会在什么时候出现呢,简而言之就是在链式赋值的时候出现。
以下例子数据以此为例:
df1 = pd.DataFrame(np.random.random(20).reshape((10,2)), columns=list('AB'))
df1
链式就是进行多次同类型的操作,比如a = b = c = 4就是一个链式操作。在这里的链式操作主要是指,对于一个pandas格式的表,选取两次或者以上次数的其中结果。
比如选取其中A值小于0.3的行得到:
df1[df1.A < 0.3]
那么选取其中所有A<0.3的B列值可以写为:
df1[df1.A < 0.3].B
以上中,先选取左右A<0.3的行,其次再从中选取B列,上述操作将其分为两部,那么这样就是链式操作。
如果此时要进行:选取其中所有A<0.3的B列值并将其赋值为1,如果进行:
df1[df1.A < 0.3].B = 1
此时就会报错SettingWithCopyWarning的Warning
如果此时再查看df1里面的值,会发现完全没有改变。
【所以此时这个爆warning是非常有意义的,如果单纯的忽略掉则会导致程序错误。】
根据会提示用loc函数。
用loc函数如下:
df1.loc[df1.A<0.3, 'B'] = 1
运行完后再查看就会发现df1里面的对应着都变为1了。
官方的解释是,pandas这个机制设计如此,凡事出现链式赋值的情况,pandas都是不能够确定到底返回的是一个引用还是一个拷贝。所以遇到这种情况就干脆报warning
有些时候比如将链式给拆解成为多步的时候,就是一些隐式的情况。
比如:
df2 = df1.loc[df1.A<0.3]
df2.loc[1,'B'] = 2
虽然这两步每步都用了loc,但是凡是把取值(GET)操作分为两步的,仍然是链式赋值的状态,所以仍然会报warning。
不过再次查看df2发现df2的值确实已经改变过来了,查看df1的值,发现df1的值没有变。
所以之前那次用loc取出的就是引用,这次就变成拷贝了。也就是说链式赋值是一个要避免的状态。
如果明确说要用拷贝怎么办,就是如下:
df2 = df1.loc[df1.A<0.3].copy()
这里总结一下pandas的这个问题: