【Python数据分析】Pandas统计分析基础,看这一篇就够了!

316 阅读15分钟

【Python数据分析】Pandas统计分析基础,看这一篇就够了!

Pandas是基于NumPy的数据分析模块,它提供了大量的数据分析会用到的工具,可以说Pnadas是Python能成为强大数据分析工具的重要原因之一。

导入方式:

import pandas as pd

Pandas中的数据结构

Pandas中包含三种数据结构:Series、DataFrame和Panel,中文翻译过来就是相当于序列、数据框和面板。

这么理解可能有点抽象,但是我们将其可以类比为:

  • Series对应数组
  • DataFrame对应表格
  • Panel对应Excel中的多表单Sheet

Series

它是一种一维数组对象,包含一个值序列,还有索引功能。

1.通过列表创建Series

import pandas as pd
obj = pd.Series([1,-2,3,-4]) # 仅仅由数组构成
print(obj)
0    1
1   -2
2    3
3   -4
dtype: int64

第一列为index索引,第二列为数据value。

当然,如果你不指定,就会默认用整形数据作为index,但是如果你想用别的方式来作为索引,你可以用别的方式。

i = ["a","b","c","d"]
v = [1,2,3,4]
t = pd.Series(v,index = i,name = "col") 
print(t)
a    1
b    2
c    3
d    4
Name: col, dtype: int64

2.通过字典创建Series

如果数据被放在字典中,就可以直接通过字典来创建Series。

# 字典创建Series
sdata = {'Joolin':20,'Jay':46}
obj2 = pd.Series(sdata)
print(obj2)
Joolin    20
Jay       46
dtype: int64

可以看到,由于字典使用键值对的方式,那么这样直接创建可以省去了创建index的操作。

当然,你也依旧可以指定你的index,那样就会覆盖原先的键。

sdata = {'Joolin':20,'Jay':46}
sp = ["Joolin","JJ"]
obj2 = pd.Series(sdata,index = sp)
print(obj2)
Joolin    20.0
JJ         NaN
dtype: float64

我们注意到,被覆盖的那些索引由于找不到与其匹配的值,就会显示**NaN,即“非数字”。或者如果你多写了没有对应值的索引元素,那么也会显示NaN**

sdata = {'Joolin':20,'Jay':46}
sp = ["Joolin","JJ","Jay"]
obj2 = pd.Series(sdata,index = sp)
print(obj2)
Joolin    20.0
JJ         NaN
Jay       46.0
dtype: float64

对于许多应用而言,Series有一个重要的功能:在算术运算中,它可以自动对齐不同索引的数据。

sdata = {'Joolin':20,'Jay':46}
states = ['Joolin','DT','Jay']
obj1 = pd.Series(sdata)
obj2 = pd.Series(sdata,index = states)
print(obj1+obj2)
DT         NaN
Jay       92.0
Joolin    40.0
dtype: float64

DataFrame

DataFrame是一个表格型的数据结构,它含有一组有序的列,每列都可以是不同的数据类型。我们来参考以下的表格进行理解:

namecityyear
0张三Beijing2001
1李四Shanghai2005
2王五Guangzhou2003

这是一个典型的表格,我们看到它包含了例如name、city、year这样的列名,并且每一行都是根据索引自动排序。DataFrame也是这样一种结构,它既有行索引也有列索引,被看作是Series组成的字典。

我们既可以通过行索引进行操作,也可以通过列索引进行操作,并且注意,它们的优先性是相同的。

1.直接通过字典创建DataFrame

一般创建的方式就是通过字典,因为毕竟键值对的方式是最符合DataFrame的特点的。

data = {
	'name' : ['张三','李四','王五'],
	'city' : ['Beijing','Shanghai','Guangzhou'],
	'year' : [2001,2005,2003]
}
df = pd.DataFrame(data)
print(df)
  name       city  year
0   张三    Beijing  2001
1   李四   Shanghai  2005
2   王五  Guangzhou  2003

我们注意,DataFrame自动给我们加上了索引,并且会被有序排列;

  • 如果我们指定列名序列,那么就会按照指定来进行列的顺序

    df1 = pd.DataFrame(data,columns = ['city','year','name'])
    
            city  year name
    0    Beijing  2001   张三
    1   Shanghai  2005   李四
    2  Guangzhou  2003   王五
    
  • 如果我们指定行名序列,也就是标签label,那么就会按照指定标签而不再使用默认的0,1,2,3

    df2 = pd.DataFrame(data,columns = ['city','year','name'],index = ['a','b','c'])
    
            city  year name
    a    Beijing  2001   张三
    b   Shanghai  2005   李四
    c  Guangzhou  2003   王五
    
  • 同Series一样,如果我们传入列在数据中找不到,那么就会产生NaN值,这里不再赘述。

索引对象

我们发现,Pandas有个很有用也很特别的东西——就是index索引,它在数据分析中可以起到很大的作用:因为数据往往都是庞大和繁杂的,如果我们直接通过数据本身来进行查找和处理,那么任务就会显得极其繁重。而如果数据有一个对应的值,或者特定的特点,那么就可以快速找到它,这就是索引

而每个索引对应的数据,就被称作索引对象

Pandas的索引对象负责管理轴标签和其他元数据,索引对象不能修改,否则会报错。也只有这样才能保证数据的准确性,并且保证索引对象在多个数据结构之间进行安全共享。

我们可以直接查看索引有哪些。

df2 = pd.DataFrame(data,columns = ['city','year','name'],index = ['a','b','c'])
print(df2.index)
print(df2.index[0])
print(df2.index[1])
print(df2.index[2])
Index(['a', 'b', 'c'], dtype='object')
a
b
c

同时索引也有它的方法和属性。

类别方法/属性描述
基本信息dtype返回索引的数据类型
size返回索引的元素数量
shape返回索引的形状
nbytes返回索引的字节数
is_unique判断索引是否唯一
is_monotonic_increasing判断索引是否单调递增
is_monotonic_decreasing判断索引是否单调递减
索引位置get_loc()返回指定值的索引位置
get_indexer()返回多个值的索引位置
slice_indexer()返回切片索引
contains()判断索引是否包含某个值
元素操作tolist()将索引转换为Python列表
map()对索引中的元素应用函数
astype()改变索引的数据类型
repeat()重复索引中的元素
drop()删除指定元素
重构rename()重命名索引
reindex()重新索引,支持缺失值填充
union()返回两个索引的并集
intersection()返回两个索引的交集
difference()返回两个索引的差集
symmetric_difference()返回两个索引的对称差集
排序sort_values()对索引进行排序
sortlevel()按多级索引的某一层排序
唯一性与重复性unique()返回索引中的唯一值
duplicated()标记重复值
其他copy()复制索引
equals()判断两个索引是否相等
append()追加一个新的索引
insert()在指定位置插入元素

查看DataFrame的常用属性

包含valuesindexcolumnsndimshape

Pandas索引操作

1.重建索引

重建索引的格式:

obj.reindex()

索引对象是无法修改的,因此,重建索引指的是:对索引重新排序而不是重新命名,如果某个索引值不存在,则会引入缺失值。

这一点其实在上述的索引操作中已经体现出来了,这里再重新复述一下。

  • 缺失值默认为NaN,但是也可以通过fill_value来进行参数的填充。
obj = pd.Series([7,5,6,8],index=['a','b','d','c'])
print(obj)
obj = obj.reindex(['a','b','c','d','e'],fill_value = 0)
print(obj)
a    7
b    5
d    6
c    8
dtype: int64
a    7
b    5
c    8
d    6
e    0
dtype: int64
  • 对于一些特殊的序列,我们可以进行向前或者向后的拷贝填充,这样就会直接给缺失的地方填上已有的值。但我们要注意,因为仅仅是前向值或者后向值的填充,那么没有被填充的地方依旧显示缺失值。

    ffillpad:前向值填充

    bfillbackfill:后向值填充

  • reindex既可以修改行索引也可以修改列索引,也可以都修改,这根据需求灵活使用即可。

2.更换索引

如果我们觉得以前的那个索引方法不适用了,不想要了,那么可以使用set_index来进行索引的更换。

df = df.set_index('city')
print(df)
          name  year
city                
Beijing     张三  2001
Shanghai    李四  2005
Guangzhou   王五  2003

注意,如果你有这种想法:我想直接更换为新的索引,比如set_index(['a','b','c'])

是不行的,因为这个方法本身只能使用已有的索引,而不能创造新的索引,会这样报错:

KeyError: "None of ['a', 'b', 'c'] are in the columns"

DataFrame数据的查询与编辑

1.DataFrame数据的查询

这里就会着重用到我们的索引功能来进行查询了。查询分为四种:选取列、选取行、选取行和列、布尔选择

A.选取列

  • 单列查询
df['column_name']        # 返回一个Series
df.column_name           			 # 等价于上面的方法
  • 多列查询
df[['col1', 'col2']]                  # 返回一个DataFrame

示例:

w1 = df['city']          # 返回一个Series
print(w1)
w2 = df.year             # 等价于上面的方法
print(w2)
w3 = df[['city','year']] # 多列查询
  • 同时,也可以使用select_dtypes等方法指定查询的范围:

示例:

w4= df.select_dtypes(exclude = 'int64').head() # 除了int64类型以外的其他列
print(w4)
  name       city
0   张三    Beijing
1   李四   Shanghai
2   王五  Guangzhou

B.选取行

  • 针对行,我们可以使用切片操作注意:列是不能使用切片操作的!
print(df[:2]) # 显示前两行
print(df[1:3]) # 显示第2-3行
  • 当然,也可以用其他方法,例如headtail方法,但它们获得的都是头部和尾部开始的数据,具有局限性。注意:当使用这两个方法不输入参数的时候,是默认前5行和后5行。

  • 也可以用sample来抽取随机样本。

C.选取行和列

鉴于切片方法选取行有很大的局限性,我们一般使用更方便的方法来进行行和列的选取。

  • loc(行索引名称或条件,列索引名称)

    # 显示name和year两列
    print(df.loc[:,['name','year']])
    # 显示北京和上海的name和year两列
    print(df.loc[['Beijing','Shanghai'],['name','year']])
    # 显示year大于等于2002的name和year两列
    print(df.loc[df['year']>=2002,['name','year']])
    

    loc与其他索引方法例如isin配合使用可以指定查询数据,有没有数据库的感觉?

  • iloc(行索引位置,列索引位置)

    # 选取前两列
    print(df.iloc[:,2])
    # 选取第1和第3行
    print(df.iloc[[0,2]])
    
  • query(self,expr,inplace = False,**kwargs)

    其中expr是要查询的字符串,kwargs是dict关键字参数。

D.布尔选择

在Pandas中可以使用逻辑运算符实现布尔选择。

2.DataFrame数据的编辑

这一部分就涉及到经典的 增删改 操作。注意在Pandas中,我们要先将数据提取出来,再进行编辑。(当然这一部分是原理,我们直接用方法就完事了)

A.增加数据

增加列

  • 直接赋值法

    data = {'name': ['Alice', 'Bob', 'Charlie'],
            'age': [25, 30, 35]}
    df = pd.DataFrame(data)
    
    # 直接添加新列
    df['city'] = ['New York', 'Los Angeles', 'Chicago']
    print(df)
    

    输出:

          name  age           city
    0    Alice   25       New York
    1      Bob   30    Los Angeles
    2  Charlie   35        Chicago
    
  • 使用assign()方法

    df = df.assign(country=['USA', 'USA', 'USA'])
    print(df)
    

    输出:

          name  age           city  age_in_5_years country
    0    Alice   25       New York               30     USA
    1      Bob   30    Los Angeles               35     USA
    2  Charlie   35        Chicago               40     USA
    

增加行

  • 使用append方法

    # 这种方法在Pandas 2.0以后已经被弃用,推荐使用concat()
    new_row = pd.DataFrame([['Eve', 27, 'Boston', 32, 'USA']], 
                           columns=df.columns)
    df = pd.concat([df, new_row], ignore_index=True)
    print(df)
    

    输出:

    name  age              city  age_in_5_years country
    0    Alice   25          New York               30     USA
    1      Bob   30       Los Angeles               35     USA
    2  Charlie   35           Chicago               40     USA
    3    David   28     San Francisco               33     USA
    4      Eve   27            Boston               32     USA
    

B.删除数据

删除数据直接使用**drop**方法,参数axis来确定删除行还是列。

注意,默认是不删除原数据,如果想要再原数据上删除应设置参数inplace = True。

# 删除单列
df1 = df.drop(columns=['city'], inplace=False)
# print(df)
print(df1)
print(df)
# 同时删除多个列
df.drop(columns=['age', 'country'], inplace=True)
print(df)
      name  age country
0    Alice   25     USA
1      Bob   30     USA
2  Charlie   35     USA

      name  age         city country
0    Alice   25     New York     USA
1      Bob   30  Los Angeles     USA
2  Charlie   35      Chicago     USA

      name         city
0    Alice     New York
1      Bob  Los Angeles
2  Charlie      Chicago

C.修改数据

修改数据直接对选择的数据赋值即可,但是注意,这里是直接修改数据的值,操作无法撤销,修改了就是修改了。

我们也可以使用replace(to_place,value)进行数据的替换

to_place是被替换的值;value是要替换成的值。

D.修改列名

直接rename()修改。注意是用键值对形式。

Pandas数据运算

算术运算

原则:有相同索引——进行算术运算;没有相同索引——进行数据对齐,并引入缺失值。

意思就是说,如果没有相同的索引,那么就不会进行运算,并且如果你原先数据位上有值,也会被当初NaN。看例子来理解:

s3 = pd.Series([100, 200, 300], index=['a', 'b', 'd'])
print(s1 + s3)
a    110.0
b    220.0
c      NaN
d      NaN
dtype: float64

针对Series和DataFrame。都会数据对齐操作,后者是会同时发生在行和列上。

df1 = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
df2 = pd.DataFrame({'A': [10, 20, 30], 'D': [40, 50, 60]})

print(df1 + df2)  # 按元素相加
    A   B   D
0  11 NaN NaN
1  22 NaN NaN
2  33 NaN NaN

函数应用和映射

如果需要进行较为复杂的运算,那么就会需要自己定义函数。如何将自己的函数使用到数据运算中呢?Pandas提供了三种方法。

  • map函数:针对Series的操作,将函数套用到每个元素中
  • apply函数:针对DataFrame的操作,将函数套用到行与列上,通过axis参数设置
  • applymap函数:针对DataFrame的操作,将函套用到每个元素中

排序

对于Series

排序有两种方法:

  • sort_index:对索引进行排序,默认为升序,ascending=False时为降序

  • sort_values:对数据进行排序。

对于DataFrame

通过指定轴的方向,使用:

  • sort_index:对行或列的索引进行排序
  • sort_values:将列名传给by参数进行列排序

汇总与统计

数据汇总

使用sum方法,默认对每列进行汇总。axis=1时对行进行汇总。

数据描述与统计

使用describe方法,会统计初步的数据特征,当然,一般都是其他方法进行更详细的描述与统计。

方法说明
count()非空值个数
sum()求和
mean()平均值
median()中位数
mode()众数
std()标准差
var()方差
min()最小值
max()最大值
idxmin()最小值的位置
idxmax()最大值的位置

数据分组与聚合

1.数据分组

1.基本使用

根据某个或几个字段对数据集进行分组,然后对每个分组进行分析与转换,这是数据分析的常见操作。

在Pandas中可以使用groupby方法来进行数据分组操作。

数据分组后返回的数据类型不再是一个数据框,而是一个groupby对象。

2.按列名分组

groupk1 = df.groupby('Salary').mean()

3.按列表或元组分组

data = {
    'Department': ['HR', 'HR', 'IT', 'IT', 'Finance', 'Finance', 'HR'],
    'Name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve', 'Frank', 'Grace'],
    'Salary': [5000, 7000, 8000, 9000, 6000, 10000, 7500],
    'Age': [25, 30, 35, 40, 28, 32, 26]
}

df = pd.DataFrame(data)

# 按部门分组
grouped = df.groupby('Department')
print(grouped.size())

4.按字典分组

通过字典作为分组键。

5.按函数分组

函数作为分组键的原理类似于字典,使用映射关系来进行分组。

数据聚合

数据聚合就是对分组后的数据进行计算,产生标量值的数据转换过程。

1.聚合函数

函数说明
sum()计算总和
mean()计算平均值
median()计算中位数
min()计算最小值
max()计算最大值
count()计算非空值的个数
std()计算标准差
var()计算方差
prod()计算乘积
first第一个值
last最后一个值

2.使用agg方法聚合数据

aggaggregate方法都是通过自定义聚合函数来进行数据的聚合。它们可以直接对DataFrame进行函数应用操作。

print(grouped['Salary'].agg([np.sum,np.mean]))
              sum    mean
Department               
Finance     16000  8000.0
HR          19500  6500.0
IT          17000  8500.0

如果希望返回的结果不以分组键为索引,可以通过as_index = False来实现。

3.分组运算

分组运算包含聚合运算,聚合运算是数据转换的特例。

  • transform方法

    可以将运算分布到每一

    data = {
        'Department': ['HR', 'HR', 'IT', 'IT', 'Finance', 'Finance', 'HR'],
        'Salary': [5000, 7000, 8000, 9000, 6000, 10000, 7500]
    }
    
    df = pd.DataFrame(data)
    
    # 计算每个部门内的工资占比
    df['Salary_Ratio'] = df.groupby('Department')['Salary'].transform(lambda x: x / x.sum())
    print(df)
    
    
      Department  Salary  Salary_Ratio
    0         HR    5000       0.256410
    1         HR    7000       0.358974
    2         IT    8000       0.470588
    3         IT    9000       0.529412
    4    Finance    6000       0.375000
    5    Finance   10000       0.625000
    6         HR    7500       0.384615
    
    
  • apply方法

    可以将运算分布到每一

    # 计算工资税后收入(假设税率20%)
    df['Net_Salary'] = df['Salary'].apply(lambda x: x * 0.8)
    
    # 计算每个部门的工资方差
    variance = df.groupby('Department')['Salary'].apply(lambda x: x.var())
    print(df)
    print(variance)
    
      Department  Salary  Net_Salary
    0         HR    5000       4000.0
    1         HR    7000       5600.0
    2         IT    8000       6400.0
    3         IT    9000       7200.0
    4    Finance    6000       4800.0
    5    Finance   10000       8000.0
    6         HR    7500       6000.0
    
    Department
    Finance    8000000.0
    HR         833333.0
    IT         500000.0
    Name: Salary, dtype: float64