目录
1.numpy
NumPy是一个在Python中广泛使用的数学和科学计算库,用于处理大型多维数组和矩阵,并提供了大量的高级数学函数来操作这些数组。
-
数组创建
NumPy使用numpy.array()
函数来创建数组。
import numpy as np
# 一维数组
arr1d = np.array([1, 2, 3, 4, 5])
print(arr1d)
# 二维数组(矩阵)
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr2d)
# 使用arange创建一维数组
arr_arange = np.arange(0, 10, 2) # 从0开始,到10(不包括),步长为2
print(arr_arange)
# 使用zeros和ones创建特定形状的数组
zeros_arr = np.zeros((3, 3)) # 3x3的零矩阵
ones_arr = np.ones((2, 2)) # 2x2的全1矩阵
print(zeros_arr)
print(ones_arr)
# 使用linspace创建等差数列
linspace_arr = np.linspace(0, 10, 5) # 从0到10,共5个点,等差分布
print(linspace_arr)
-
数组索引和切片
NumPy数组支持Python的索引和切片语法。
# 索引
print(arr2d[0, 0]) # 访问第一行第一列的元素
# 切片
print(arr2d[0:2, 1:3]) # 访问前两行,第二列到第三列(不包括第三列)的元素
-
数组形状和维度
使用.shape
属性查看数组的形状,.ndim
属性查看数组的维度。
print(arr2d.shape) # 输出:(3, 3)
print(arr2d.ndim) # 输出:2
-
数组运算
NumPy支持元素级运算、广播等。
# 元素级运算
result = arr2d + 1 # 每个元素都加1
print(result)
# 广播(Broadcasting)
arr_broadcast = np.array([[1], [2], [3]])
print(arr2d + arr_broadcast) # 3x3的矩阵与3x1的矩阵相加,后者会广播到3x3
-
数组统计
NumPy提供了很多统计函数,如mean()
, std()
, max()
, min()
等。
print(arr2d.mean()) # 计算所有元素的平均值
print(arr2d.std()) # 计算所有元素的标准差
print(arr2d.max()) # 计算所有元素的最大值
print(arr2d.min()) # 计算所有元素的最小值
-
线性代数
NumPy还提供了线性代数相关的函数,如矩阵乘法、逆矩阵、行列式等。
# 矩阵乘法
mat_mul = np.dot(arr2d, arr2d.T) # arr2d与arr2d的转置相乘
print(mat_mul)
# 逆矩阵
inv_mat = np.linalg.inv(arr2d) # 注意:arr2d需要是可逆的
print(inv_mat)
# 行列式
det = np.linalg.det(arr2d)
print(det)
-
排序和搜索
NumPy提供了对数组进行排序和搜索的函数。
# 排序
sorted_arr = np.sort(arr1d)
print(sorted_arr)
# 搜索(返回元素在数组中的索引)
index = np.where(arr1d == 3)
print(index)
2.pandas
pandas提供了高效操作大型数据集所需的工具,是使python能够成为高效且强大的数据分析环境的重要因素之一。
pandas有三种数据结构:series、dataframe、panel。
series相当于表格的一列
dataframe相当于表格
panel相当于多张表格
2.1 series操作
下面将会介绍:如何创建series、series的索引、自动对齐不同索引的数据……
-
series的创建
从列表创建:Series可以像列表一样包含任何数据类型(整数、浮点数、字符串等)
import pandas as pd
data_list = [1, 2, 3, 4, 5]
s = pd.Series(data_list)
print(s)
从字典创建:字典的键成为Series的索引,字典的值成为Series的数据
data_dict = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
s = pd.Series(data_dict)
print(s)
从NumPy数组创建:同时指定索引和数据
import numpy as np
data_array = np.array([1, 2, 3, 4, 5])
s = pd.Series(data_array, index=['A', 'B', 'C', 'D', 'E'])
print(s)
-
series的索引和切片
-
隐式索引(位置):如果不指定索引,pandas会自动为Series分配默认的整数索引。
-
显示索引(标签):通过指定
index
参数,可以创建具有自定义索引的Series。 -
切片:使用Python的切片语法访问Series的子集。
s = pd.Series([1, 2, 3, 4, 5], index=['A', 'B', 'C', 'D', 'E'])
print(s['A':'C']) # 基于标签的切片
print(s[0:3]) # 基于位置的切片(不推荐,因为可能会与整数索引混淆)
但是当标签也是数字时,位置就无法引用了。
-
Series的基本操作
-
访问元素:通过索引或位置访问Series中的元素。
-
修改元素:可以直接通过索引为Series的元素赋值。
-
算术运算:Series支持各种算术运算,如加法、减法、乘法、除法等。
-
统计函数:Series提供了许多统计函数,如
mean()
,sum()
,std()
,min()
,max()
等。
s = pd.Series([1, 2, 3, 4, 5], index=['A', 'B', 'C', 'D', 'E'])
print(s['A']) # 访问元素
s['A'] = 10 # 修改元素
print(s)
print(s.mean()) # 计算平均值
print(s.add(s)) # 元素间的加法
-
Series的缺失数据处理
-
NaN值:在pandas中,缺失数据通常用NaN(Not a Number)表示。
-
处理NaN值:pandas提供了许多函数和方法来处理NaN值,如
dropna()
,fillna()
,interpolate()
等。
s = pd.Series([1, 2, np.nan, 4, 5], index=['A', 'B', 'C', 'D', 'E'])
print(s.dropna()) # 删除包含NaN的行
print(s.fillna(0)) # 用0填充NaN值
-
series自动对齐不同索引
当我们在使用pandas中的Series进行算术运算时,如果两个Series的索引不完全相同,pandas会尝试自动对齐它们的索引。缺失的索引值会被视为NaN
,并且这些NaN
值会在运算中按照数学规则进行处理(例如,在加法中,任何数与NaN
相加结果都是NaN
)。
import pandas as pd
# 创建两个具有不同索引的Series
s1 = pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])
s2 = pd.Series([10, 20, 30], index=['b', 'c', 'e'])
# 进行加法运算,注意索引'a', 'd', 和 'e'的值是NaN,因为它们只出现在一个Series中
result = s1 + s2
print(result)
a NaN
b 21.0
c 33.0
d NaN
e NaN
dtype: float64
在这个例子中,s1
和s2
的索引不完全相同。当我们对它们进行加法运算时,pandas会尝试自动对齐索引。对于存在于两个Series中的索引(如'b'和'c'),相应的值会相加。对于只存在于一个Series中的索引(如'a', 'd', 和 'e'),结果Series中对应位置的值会是NaN
。这是因为在进行算术运算时,pandas不知道如何处理缺失的索引值,所以默认将它们视为NaN
。
需要注意的是,如果希望在进行算术运算时填充缺失值(例如,用0填充),可以使用fillna()
方法或者在运算时指定fill_value
参数。例如:
result = s1.add(s2, fill_value=0)
print(result)
这样,所有缺失的索引值在运算时都会被当作0来处理。
-
series实际应用
设想一个场景,其中我们需要记录和分析一个销售团队的业绩。假设我们有每个销售员的姓名和他们的销售额数据,我们将使用这些数据来创建Series。
import pandas as pd
# 假设我们有以下销售员和他们的销售额数据
sales_data = {
'Alice': 15000,
'Bob': 20000,
'Charlie': 18000,
'David': 12000
}
# 假设我们还有一个包含所有销售员姓名的列表,但顺序可能不同
salespeople = ['Bob', 'David', 'Eve', 'Charlie']
# 1. 使用字典直接创建Series
sales_series = pd.Series(sales_data)
print("使用字典创建的Series:")
print(sales_series)
print("\nSeries的标签(索引):", sales_series.index)
print("Series的位置(可以使用iloc访问):")
for i in range(len(sales_series)):
print(f"位置 {i}: {sales_series.iloc[i]}")
# 2. 使用列表和索引创建Series(这里我们仅使用销售员列表作为索引,数据用随机数填充)
import numpy as np
random_sales = np.random.randint(10000, 25000, size=len(salespeople))
indexed_sales_series = pd.Series(random_sales, index=salespeople)
print("\n使用列表和索引创建的Series:")
print(indexed_sales_series)
# 3. 展示自动对齐:当我们试图将两个Series相加时,pandas会自动对齐它们的索引
# 假设Eve的销售额为16000(我们将其添加到原始字典中)
sales_data['Eve'] = 16000
updated_sales_series = pd.Series(sales_data)
# 将indexed_sales_series和updated_sales_series相加,注意Eve在indexed_sales_series中没有值,但pandas会将其视为NaN
combined_sales = indexed_sales_series.add(updated_sales_series, fill_value=0)
print("\n自动对齐索引并相加的Series:")
print(combined_sales)
# 可以看到Eve的销售额被正确添加,而其他销售员的销售额则是原始值和随机值的和(或0,如果原始值不存在)
2.2 dataframe操作
dataframe是一个表格型的数据结构,包括一组有序的列。每一列可以是不同类型的值。它可以看作是由很多列series组成的表格。
dataframe既有行索引,也有列索引。列索引对应着字典的键,行索引每一列共用。
-
创建DataFrame
1.使用字典创建:将字典作为数据源,其中键作为列名,值作为对应列的数据。
import pandas as pd
data = {'A': [1, 2, 3], 'B': [4, 5, 6]}
df = pd.DataFrame(data)
2.使用嵌套列表创建:通过嵌套列表创建,并指定列名。
data = [[1, 4], [2, 5], [3, 6]]
df = pd.DataFrame(data, columns=['A', 'B'])
3.从CSV文件创建:读取CSV文件中的数据,并创建DataFrame。
df = pd.read_csv('file.csv')
-
索引和切片
在Pandas的DataFrame
中,索引(Index)分为行索引(通常称为index
)和列索引(通常称为columns
)。
1.通过索引获取数据
1.1使用列标签或行标签索引:通过列名或行索引来获取数据。
value = df['A'][0] # 获取A列第0行的值
1.2布尔索引:使用条件表达式筛选数据。
filtered_df = df[df['A'] > 1] # 筛选A列大于1的行
2.定义索引
默认情况下,当你创建一个新的DataFrame
时,Pandas会自动为你分配行索引和列索引。但是,你也可以在创建时或之后自定义这些索引。
2.1定义列索引:列索引是DataFrame中列的名称。在创建DataFrame
时,你可以通过传递一个字典(其中键是列名,值是列的数据)或一个包含列名的列表来定义列索引。
import pandas as pd
data = {
'A': [1, 2, 3],
'B': [4, 5, 6],
'C': [7, 8, 9]
}
df = pd.DataFrame(data)
print(df)
A B C
0 1 4 7
1 2 5 8
2 3 6 9
2.2定义行索引:行索引是DataFrame中行的标签。默认情况下,当你创建一个新的DataFrame
时,Pandas会使用整数作为行索引,从0开始递增。但是,你也可以在创建时或之后自定义行索引。
import pandas as pd
data = {
'A': [1, 2, 3],
'B': [4, 5, 6],
'C': [7, 8, 9]
}
index = ['row1', 'row2', 'row3']
df = pd.DataFrame(data, index=index)
print(df)
A B C
row1 1 4 7
row2 2 5 8
row3 3 6 9
3.修改索引
3.1修改列索引
使用.rename(columns=...)
方法或.set_axis(axis=1, ...)
方法来进行修改,或直接给DataFrame.columns
赋值。
# 修改列索引
df.columns = ['C', 'D']
print(df)
# 或者使用.rename()方法
df = df.rename(columns={'C': 'E', 'D': 'F'})
print(df)
首先创建了一个包含三列数据的DataFrame。然后,我们创建了一个新的Index
对象new_columns
,其中包含了我们想要设置的新列名('A', 'B', 'C')。最后,我们使用.set_axis()
方法将新的列索引应用到DataFrame上,其中axis=1
指定我们正在设置列索引。
虽然在这个例子中,我们也可以使用df.columns = ['A', 'B', 'C']
来达到同样的效果,但.set_axis()
方法提供了一种更灵活的方式来设置轴标签,特别是当你需要从另一个Index
对象或类似的可迭代对象中获取标签时。
.set_axis()
方法会返回一个新的DataFrame,而不会修改原始DataFrame(除非你在调用时使用了inplace=True
参数
import pandas as pd
# 创建一个简单的DataFrame
data = {
'col1': [1, 2, 3],
'col2': [4, 5, 6],
'col3': [7, 8, 9]
}
df = pd.DataFrame(data)
# 创建一个新的Index对象作为新的列索引
new_columns = pd.Index(['A', 'B', 'C'])
# 使用.set_axis()方法设置列索引
df1 = df.set_axis(new_columns, axis=1)
#行索引(axis=0)或列索引(axis=1)
print(df1)
A B C
0 1 4 7
1 2 5 8
2 3 6 9
3.2修改行索引
行索引是DataFrame中每一行的标签。可以通过重新赋值来修改它。但是,直接修改DataFrame.index
可能会导致一些意外的行为,特别是当你试图将一个非唯一索引或具有重复标签的索引分配给DataFrame时。
通常,要修改行索引,你可以使用.rename()
方法(对于单个索引)或.set_index()
方法(对于将现有列设置为索引)。另外,你也可以直接给DataFrame.index
赋值一个新的Index对象。
# 假设'A'列现在是行索引
df.set_index('A', inplace=True)
print(df)
import pandas as pd
# 创建一个简单的DataFrame
data = {
'A': [1, 2, 3],
'B': [4, 5, 6]
}
index = ['old_index1', 'old_index2', 'old_index3']
df = pd.DataFrame(data, index=index)
# 创建一个字典,用于映射旧的行索引到新的行索引
mapper = {
'old_index2': 'new_index2'
}
# 使用.rename()方法重命名行索引
# 注意:这里我们需要指定axis=0(或'index'),因为默认是重命名列(axis=1或'columns')
df_renamed = df.rename(index=mapper)
#df_renamed = df.rename(index={'old_index2': 'new_index2' })
print(df_renamed)
import pandas as pd
# 创建一个简单的DataFrame
df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]}, index=['x', 'y', 'z'])
# 修改行索引
df.index = ['a', 'b', 'c']
print(df)
3.3注意事项
- 修改索引时,请确保新的索引是唯一的,否则可能会导致数据对齐问题。
- 修改索引可能会导致基于原始索引的索引器(如
.loc[]
)失效。 - 修改索引可能会影响DataFrame的性能,特别是当DataFrame很大时。
- 在进行任何索引修改操作之前,最好先备份原始数据,以防万一需要回滚更改。
4.索引属性
5.loc和iloc
在Python的pandas库中,loc
和 iloc
是两种常用的索引方法,用于从DataFrame或Series中选取数据。虽然它们都用于索引,但它们的工作原理和使用方式有所不同。
-
loc
loc
方法基于标签进行索引。它接受行标签和列标签作为参数,并返回对应位置的数据。
import pandas as pd
# 创建一个简单的DataFrame
df = pd.DataFrame({
'A': [1, 2, 3],
'B': [4, 5, 6],
'C': [7, 8, 9],
'index': ['x', 'y', 'z']
}, index=['x', 'y', 'z'])
# 使用loc按标签选择单列
print(df.loc[:, 'A']) # 返回列'A'的数据
# 使用loc按标签选择多列
print(df.loc[:, ['A', 'B']]) # 返回列'A'和'B'的数据
# 使用loc按标签选择单行
print(df.loc['x']) # 返回行'x'的数据
# 使用loc按标签选择多行
print(df.loc[['x', 'y']]) # 返回行'x'和'y'的数据
# 使用loc按标签选择特定行列组合
print(df.loc['x', 'A']) # 返回行'x'列'A'的数据
-
iloc
iloc
方法基于整数位置进行索引。它接受行号和列号(基于0的索引)作为参数,并返回对应位置的数据。
import pandas as pd
# 创建一个简单的DataFrame
df = pd.DataFrame({
'A': [1, 2, 3],
'B': [4, 5, 6],
'C': [7, 8, 9]
})
# 使用iloc按位置选择单列
print(df.iloc[:, 0]) # 返回第一列的数据
# 使用iloc按位置选择多列
print(df.iloc[:, [0, 1]]) # 返回第一列和第二列的数据
# 使用iloc按位置选择单行
print(df.iloc[0]) # 返回第一行的数据
# 使用iloc按位置选择多行
print(df.iloc[[0, 1]]) # 返回第一行和第二行的数据
# 使用iloc按位置选择特定行列组合
print(df.iloc[0, 0]) # 返回第一行第一列的数据
- 注意事项
loc
和iloc
都接受切片操作,如df.loc['x':'y']
或df.iloc[0:2]
。- 当使用
loc
时,如果DataFrame的索引不是整数或连续的,你可能需要使用.reset_index(drop=True)
来重置索引为默认的整数索引,然后再使用iloc
。 下面假设这个3行3列的dataframe对象有列索引x、y、z。索引不是整数,不能直接使用
iloc
来索引标签'x'
、'y'
或'z'
。iloc
是基于位置的索引,它总是使用基于0的整数来引用行和列。然而,如果你确实想使用
iloc
(或者基于位置的索引)来引用这个DataFrame的行,你可以通过reset_index
方法来重置索引。reset_index
方法会创建一个新的DataFrame,其索引是默认的整数索引(从0开始)。drop=True
参数意味着原始索引(在这个例子中是['x', 'y', 'z']
)不会作为一列加入到新的DataFrame中。 iloc
总是基于整数位置进行索引,即使你的DataFrame的索引是字符串或其他类型。- 在选择多行或多列时,可以使用列表(如
['A', 'B']
或[0, 1]
)来指定要选择的列或行的标签或位置。
2.3 pandas数据运算
函数应用与映射
在pandas
库中,map()
, apply()
, 和 applymap()
是非常有用的函数,它们允许你对DataFrame或Series中的数据应用函数。
-
map()
函数
map()
函数是pandas.Series
的一个方法,它用于将一个函数应用到Series的每一个元素上。它也可以接受一个字典或者Series作为参数,以执行替换操作。
import pandas as pd
s = pd.Series(['cat', 'dog', 'elephant', 'cat'])
def animal_sound(animal):
if animal == 'cat':
return 'meow'
elif animal == 'dog':
return 'woof'
elif animal == 'elephant':
return 'toot'
else:
return 'unknown'
s_sounds = s.map(animal_sound)
print(s_sounds)
# 输出:
# 0 meow
# 1 woof
# 2 toot
# 3 meow
# dtype: object
sounds_mapping = {'cat': 'meow', 'dog': 'woof', 'elephant': 'toot'}
s_sounds = s.map(sounds_mapping)
print(s_sounds)
# 输出同上
apply()
函数
apply()
函数既可以用于pandas.Series
也可以用于pandas.DataFrame
。对于Series,它的功能与map()
类似,但apply()
更通用,因为它可以接受任何函数,而不仅仅是字典或Series到Series的映射。对于DataFrame,apply()
可以用于沿着行(axis=1
)或列(axis=0
)应用函数。
s = pd.Series([1, 2, 3, 4])
def square(x):
return x ** 2
s_squared = s.apply(square)
print(s_squared)
# 输出:
# 0 1
# 1 4
# 2 9
# 3 16
# dtype: int64
df = pd.DataFrame({
'A': [1, 2, 3, 4],
'B': [5, 6, 7, 8]
})
def sum_of_squares(row):
return row['A'] ** 2 + row['B'] ** 2
df['C'] = df.apply(sum_of_squares, axis=1)
print(df)
# 输出:
# A B C
# 0 1 5 26
# 1 2 6 40
# 2 3 7 58
# 3 4 8 80
applymap()
函数
applymap()
函数是pandas.DataFrame
的一个方法,它用于将函数应用到DataFrame的每一个元素上。
df = pd.DataFrame({
'A': [1, 2, 3],
'B': [4, 5, 6]
})
def add_one(x):
return x + 1
df_plus_one = df.applymap(add_one)
print(df_plus_one)
# 输出:
# A B
# 0 2 5
# 1 3 6
# 2 4 7
- 总结
map()
适用于Series,特别是当你需要用一个字典或函数进行映射时。apply()
可用于Series和DataFrame,它允许你沿着行或列应用函数。applymap()
专用于DataFrame,它将函数应用到DataFrame的每一个元素上。
排序
在Pandas中,sort_index()
和sort_values()
是用于对DataFrame和Series进行排序的两种重要方法。
-
sort_index()
sort_index()
方法用于根据索引对DataFrame或Series进行排序。
默认情况下,排序是升序的(ascending=True)。
可以指定排序的方向(ascending参数),True为升序,False为降序。
对于DataFrame,可以通过指定axis参数(默认为0)来指定按行(axis=0)或按列(axis=1)进行排序。
对于MultiIndex,可以通过指定level参数来指定按哪个级别的索引进行排序。
import pandas as pd
# 创建一个带有索引的Series
s = pd.Series(['a', 'b', 'c', 'd'], index=[3, 1, 4, 2])
# 使用sort_index()对Series进行排序
sorted_s = s.sort_index()
print(sorted_s)
# 输出:
# 1 b
# 2 d
# 3 a
# 4 c
# dtype: object
# 创建一个带有索引的DataFrame
df = pd.DataFrame({
'A': [1, 2, 3, 4],
'B': [5, 6, 7, 8]
}, index=['d', 'b', 'c', 'a'])
# 使用sort_index()对DataFrame的行进行排序
sorted_df_rows = df.sort_index()
print(sorted_df_rows)
# 输出:
# A B
# a 4 8
# b 2 6
# c 3 7
# d 1 5
# 使用sort_index()对DataFrame的列进行排序(注意这里我们设置了axis=1)
sorted_df_cols = df.sort_index(axis=1)
print(sorted_df_cols)
# 注意:对于列来说,索引实际上就是列名,所以这个操作不会改变DataFrame的内容,只是显示了列的顺序。
sort_values()
sort_values()
方法用于根据值对DataFrame或Series进行排序。
可以指定一个或多个列/索引标签(by参数)来指定根据哪些值进行排序。
可以指定排序的方向(ascending参数),True为升序,False为降序。
对于DataFrame,默认情况下是按列进行排序的,但也可以通过设置axis=1来按行排序(但通常不这么用,因为按行排序不太直观)。
可以指定inplace参数,如果为True,则直接在原始对象上进行排序,不返回新对象。
可以指定na_position参数来处理缺失值('first'或'last')。
# 创建一个Series
s = pd.Series([4, 2, 3, 1])
# 使用sort_values()对Series进行排序
sorted_s = s.sort_values()
print(sorted_s)
# 输出:
# 0 1
# 3 2
# 2 3
# 1 4
# dtype: int64
# 创建一个DataFrame
df = pd.DataFrame({
'A': [1, 2, 3, 4],
'B': [4, 3, 2, 1]
})
# 使用sort_values()根据列'A'的值对DataFrame进行排序
sorted_df_A = df.sort_values(by='A')
print(sorted_df_A)
# 输出:
# A B
# 0 1 4
# 1 2 3
# 2 3 2
# 3 4 1
# 使用sort_values()根据列'A'和'B'的值对DataFrame进行排序(先按'A'排序,然后在'A'相同的情况下按'B'排序)
sorted_df_AB = df.sort_values(by=['A', 'B'])
print(sorted_df_AB)
# 输出与sorted_df_A相同,因为在这个例子中'A'的值已经是唯一的了
# 对多列
汇总与统计
在Pandas中,数据汇总和统计是常见的任务,Pandas提供了多种方法来进行这些操作。以下是一些常用的数据汇总和统计方法,以及它们的使用示例:
describe()
:
这个方法用于生成描述性统计信息,默认针对数值型列。包括:count(非空值数量)、mean(平均值)、std(标准差)、min(最小值)、25%、50%(中位数)、75%分位数和max(最大值)。
import pandas as pd
data = {'A': [1, 2, 3, 4, 5],
'B': [5, 4, 3, 2, 1],
'C': ['a', 'b', 'c', 'd', 'e']}
df = pd.DataFrame(data)
print(df.describe())
# 输出A和B两列的统计信息
sum():
在Pandas中,sum()
方法通常用于计算DataFrame或Series中所有数值的和。这个方法可以沿着行(axis=1
)或列(axis=0
,默认)进行计算。
- 其他:
- 频数统计
在Pandas中,对于类别型(Categorical)或离散型(Discrete)特征,我们通常使用频数统计表来描述数据的分布情况。这种统计表显示了每个唯一值(unique value)在数据集中出现的次数。Pandas提供了两个非常有用的方法来创建这样的统计表:unique()
和 value_counts()
。
unique()
unique()
是一个返回唯一值的数组的函数,仅提供不重复的数值或类别列表。
nunique()
nunique()
是一个非常方便的方法,用于快速了解数据集中不同值的数量
dropna
: 默认为True
,表示在计算不同元素数量时不考虑NaN值。如果设置为False
,NaN将被视为一个独特的值。
axis
: 在DataFrame上使用时可指定。0
或 'index'
表示按列操作(默认)1
或 'columns'
表示按行操作。
当应用于DataFrame时,nunique()
默认会针对每一列分别计算不同元素的数量。如果应用于Series,它将返回该Series中不同元素的数量。
import pandas as pd
# 创建一个DataFrame
df = pd.DataFrame({
'Name': ['Alice', 'Bob', 'Charlie', 'Alice', 'Bob'],
'Age': [25, 30, 35, 40, 25],
'City': ['New York', 'Los Angeles', 'New York', 'Chicago', 'Los Angeles']
})
# 使用nunique()计算每列的唯一值数量
unique_counts = df.nunique()
print(unique_counts)
Name 3
Age 4
City 3
dtype: int64
import pandas as pd
# 创建一个Series
s = pd.Series(['cat', 'dog', 'cat', 'bird', 'dog', 'elephant'])
# 使用nunique()计算Series中不同元素的数量
unique_count = s.nunique()
print(unique_count)
4
import pandas as pd
# 创建一个包含NaN值的Series
s = pd.Series(['cat', 'dog', None, 'bird', 'dog', 'elephant'])
# 使用nunique()并设置dropna=False
unique_count_with_nan = s.nunique(dropna=False)
print(unique_count_with_nan) # 输出将包括NaN作为一个独特值
5
import pandas as pd
# 创建一个DataFrame
df = pd.DataFrame({
'A': [1, 2, 3],
'B': [1, 2, 1],
'C': [2, 2, 3]
})
# 使用nunique(),设置axis=1,按行计算唯一值的数量
row_unique_counts = df.nunique(axis=1)
print(row_unique_counts)
0 2
1 2
2 2
dtype: int64
value_counts()
value_counts()
是一个更强大的函数,它返回每个唯一值在Series中的计数,并按计数降序排列。它对于类别型特征的描述性统计非常有用。
在这个例子中,我们可以看到类别'A'出现了3次,类别'B'出现了2次,类别'C'和'D'各出现了1次。
频数统计表在数据分析中非常有用,因为它们可以帮助我们:快速识别最常见的类别,发现稀有或异常的类别,了解数据分布的均衡性。通过value_counts()
得到的频数统计表,我们可以进一步对数据进行可视化(例如,使用柱状图),以便更直观地展示类别分布。关于matplotlib将在以后的章节介绍。
分组操作
在Pandas中,分组(Grouping)与聚合(Aggregation)操作是非常强大的工具,允许你根据一个或多个列对数据进行分组,并在每个组内执行聚合函数。这些操作通常使用groupby()
方法和一些聚合函数来完成。
groupby()
groupby()
方法接受一个或多个列名作为参数,并返回一个 DataFrameGroupBy
对象,该对象可以进一步用于聚合或其他操作。
假设我们有一个DataFrame,其中包含一些学生的信息以及他们的成绩:
根据一个列进行分组:
如果你想计算每个科目的平均分,你可以这样做:
这是Pandas中的groupby
方法,它用于根据指定的列(在这个例子中是'Subject'列)将数据分组。当你对数据调用groupby('Subject')
时,Pandas会创建一个分组对象,这个对象会记住如何根据'Subject'列的值将数据分组,但并不会立即执行任何计算或聚合。
这个分组对象允许你进一步指定你想在哪个列(或哪些列)上执行聚合操作。
在groupby
方法之后,你使用方括号[]
来选择你希望进行聚合的列。在这个例子中,你选择了'Score'列。这告诉Pandas你只对'Score'列感兴趣,并希望在该列上执行聚合操作。
注意,这里['Score']
并不是在分组对象上调用一个方法或函数,而是在选择分组对象中的某个特定部分(即'Score'列)。这就是为什么使用方括号而不是圆括号。
最后,你调用mean()
方法来计算每个组中'Score'列的平均值。这个mean()
方法是直接作用在之前通过['Score']
选择出来的'Score'列上的。
所以,整个表达式df.groupby('Subject')['Score'].mean()
的意思是:首先根据'Subject'列的值将数据分组,然后选择'Score'列,并在每个组内计算'Score'列的平均值。
当你执行这个表达式后,你会得到一个Series对象,其中索引是唯一的'Subject'值(即不同的学科),而数据是每个学科'Score'列的平均值。
如果想同时根据“Name”和“Subject”进行分组,并计算每个组内的平均分,你可以这样做:
DataFrame df
,它有三列:'Name'、'Subject' 和 'Score'。这些列分别代表学生的名字、科目和成绩。
groupby(['Name', 'Subject']):这是 groupby
方法的调用,它的作用是按照指定的列对数据进行分组。在这个例子中,我们按照 'Name' 和 'Subject' 两列进行分组。这意味着,对于数据中的每一对不同的(名字,科目)组合,groupby
都会创建一个新的组。
例如,对于名字 "Alice" 和科目 "Math",所有这样的行都会被分到一个组中;对于名字 "Bob" 和科目 "Science",所有这样的行会被分到另一个组中,以此类推。
['Score']:在 groupby
方法调用之后,我们使用索引 ['Score']
来选择我们想要对其执行操作的列。在这个例子中,我们只对 'Score' 列感兴趣,因为我们要计算每个(名字,科目)组合的平均成绩。
注意:虽然 groupby
是基于 'Name' 和 'Subject' 两列进行分组的,但我们在后续操作中只选择了 'Score' 列。这并不意味着 'Name' 和 'Subject' 的信息被丢弃了;相反,这些信息仍然被用来确定哪些行属于同一个组。
mean():这是一个聚合函数,用于计算每个组的平均值。因为我们之前选择了 'Score' 列,所以 mean()
函数会计算每个(名字,科目)组合的平均成绩。
grouped
是一个 Series
对象,它的索引是唯一的(名字,科目)组合,值是这些组合对应的平均成绩。你可以将其视为一个字典,其中键是(名字,科目)对,值是平均成绩。但是,由于它是 pandas
的 Series
对象,所以它提供了许多方便的方法和属性来查询和操作数据。
你还可以使用 agg()
方法对每个组执行多个聚合操作。例如,计算每个科目的总分、平均分和最大分:
除了聚合外,你还可以使用 transform()
方法对每个组执行转换操作,这会将结果广播到与原始DataFrame形状相同的DataFrame中。
例如,计算每个学生的成绩与所在科目平均分的差值:
这行代码主要做了两件事情:
- 计算每个科目('Subject')的平均分。
- 创建一个新的列 'Score_Diff',其中包含每个学生的分数与其所在科目平均分的差值。
1.计算每个科目的平均分
使用 groupby('Subject')
groupby('Subject')
是对数据集 df
按照 'Subject' 列进行分组。这意味着所有数学('Math')的成绩会被分为一组,所有科学('Science')的成绩会被分为另一组。
使用 ['Score']
['Score']
选择了我们想要进行操作的列,即分数列。
使用 transform('mean')
transform('mean')
是一个特殊的操作,它会对每个组(即每个科目)计算一个值(这里是平均值),但结果会被广播(broadcast)到与原始DataFrame df
形状相同的DataFrame中。换句话说,对于每个科目,它会计算平均分,但结果不会是一个单独的值或一个汇总的Series,而是一个与原始 'Score' 列形状相同的Series,其中每个属于同一科目的值都被替换为该科目的平均分。
2. 创建新的列 'Score_Diff'
现在,我们有了每个学生的分数和他们所在科目的平均分(作为与原始DataFrame相同形状的Series)。我们可以简单地将每个学生的分数减去他们所在科目的平均分来得到差值。
使用 df['Score'] - ...
这里,我们直接取 df['Score']
(即原始分数列),并从每个分数中减去上面计算得到的平均分(由 transform('mean')
给出)。结果是一个新的Series,其中包含了每个学生的分数与其所在科目平均分的差值。
赋值给新的列 df['Score_Diff']
最后,我们将这个差值Series赋值给DataFrame df
的一个新列 'Score_Diff'。
聚合操作
一旦你使用groupby()
方法将数据分组,你就可以对每个组执行聚合操作了。这通常通过调用聚合函数(如sum()
, mean()
, count()
, min()
, max()
等)来完成。你也可以使用agg()
或apply()
方法来应用自定义函数。
groupby()
方法返回一个特殊的DataFrameGroupBy
或SeriesGroupBy
对象,该对象包含原始数据的分组信息,但并未实际执行任何聚合操作。你需要调用聚合函数(如sum()
, mean()
等)来执行聚合操作。
使用agg聚合函数
对于agg和aggregate都支持对每个分组应用某个函数,包括python内置函数或自定义函数。同时,这两个方法也能够直接对dataframe进行函数应用操作。在正常使用中,agg和aggregate对dataframe的操作功能基本相同,因此只需掌握一个即可。
我们假造一份数据如下,使用agg进行分析:
两个方括号用于选择多个列,而一个方括号通常用于选择单个列或进行基于条件的索引。
使用一个方括号是因为你要么是在选择一个单独的列(返回一个Series),要么是在使用.loc
或.iloc
选择行和列,或者是在进行条件索引。
当你使用df[['淋巴细胞计数', "白细胞计数"]]
时,你正在从DataFrame df
中选择名为'淋巴细胞计数'和'白细胞计数'的两列。这会返回一个包含这两列的新的DataFrame。
这首先选择了'淋巴细胞计数'和'白细胞计数'两列,然后对这些列应用了np.sum
和np.mean
两个函数。结果是一个包含每列总和和平均值的新的DataFrame。
只使用一个方括号的情况通常出现在以下几种场景中:
1.选择单个列
single_column = df['淋巴细胞计数'] # 返回一个Series
2.使用loc
或iloc
选择行和列(其中列是一个列表时):
selected_rows_columns = df.loc[df.index[0:3], ['淋巴细胞计数', "白细胞计数"]] # 选择前3行和两列
3.使用条件索引(布尔索引):
filtered_df = df[df['淋巴细胞计数'] > 1000] # 选择淋巴细胞计数大于1000的所有行
如果希望返回的结果不以分组键为索引,可以通过as_index=False实现
补充
transform方法
pandas
中的 transform
方法是一个非常有用的工具,它允许你应用一个函数到一个 DataFrame
或 Series
的每个分组,但不同于 apply
,transform
会返回与原始对象相同形状的对象,即它保留原始索引和分组结构。
当你在 groupby
对象上调用 transform
时,你可以将函数应用于每个分组,但结果仍然是一个与原始数据形状相同的 DataFrame
或 Series
。
import pandas as pd
# 创建一个示例 DataFrame
data = {
'employee': ['Alice', 'Bob', 'Alice', 'Bob', 'Charlie', 'Charlie'],
'sales': [100, 200, 150, 300, 250, 350]
}
df = pd.DataFrame(data)
# 使用 transform 方法计算每个员工的平均销售额
# 并将结果作为新列添加到原始 DataFrame
df['avg_sales'] = df.groupby('employee')['sales'].transform('mean')
print(df)
employee sales avg_sales
0 Alice 100 125.0
1 Bob 200 250.0
2 Alice 150 125.0
3 Bob 300 250.0
4 Charlie 250 300.0
5 Charlie 350 300.0
apply方法
apply方法类似于agg方法,能够将函数应用于每一列。
如果希望返回的结果不以分组键为索引,设置group_keys=False即可。但是apply不能像agg一样实现对不同字段应用不同的函数。
2.4 数据透视表
透视表
数据透视表(Pivot Table)是一种数据分析工具,它可以从一个大的数据集(如数据库、电子表格等)中总结和提取信息,并按一种或多种分类(或称为“维度”)对数据进行分组和汇总。在数据分析中,数据透视表是一种强大的工具,因为它可以快速创建摘要报告,以分析大量数据并发现数据中的模式、趋势、异常等。
在Python的pandas库中,我们可以使用pivot_table
方法来创建数据透视表。
pandas.pivot_table(data, values=None, index=None, columns=None, aggfunc='mean', margins=False, dropna=True, margins_name='All')
dropna:表示是否删掉全为NaN的列。
margins:表示汇总功能的开关。
交叉表
交叉表是数据透视表的一种特殊情况,它主要用于计算两个或多个分类变量的频数或百分比。在pandas中,可以使用crosstab
函数来创建交叉表。
pandas.crosstab(index, columns=None, rownames=None, colnames=None, aggfunc=None, margins=False, margins_name='All', dropna=True, normalize=False)
2.5 pandas数据载入与预处理
读取数据
Pandas支持多种文件格式,包括CSV、Excel、SQL、JSON、HDF5等。
import pandas as pd
df = pd.read_csv('file.csv')
常见参数:sep
(分隔符,默认为,
)、header
(是否包含表头,默认为0
表示第一行是表头)、index_col
(将某一列设置为索引)。
df = pd.read_excel('file.xlsx', sheet_name='Sheet1')
常用参数:sheet_name
(工作表名或索引)、header
、index_col
。
import sqlite3
conn = sqlite3.connect('file.db')
df = pd.read_sql_query("SELECT * FROM table_name", conn)
conn.close()
存储数据
df.to_csv('output.csv', index=False) # index=False表示不保存索引
df.to_excel('output.xlsx', sheet_name='Sheet1', index=False)
存入数据库
import sqlite3
conn = sqlite3.connect('file.db')
df.to_sql('table_name', conn, if_exists='replace', index=False)
conn.close()
数据合并
Pandas提供了多种方法用于合并数据,包括merge()
、concat()
和combine_first()
。
- merge() :基于一个或多个键将两个DataFrame合并在一起。
参数:
how:表示连接方法,inner,outer,left,right。相当于数据库中连接,外连接:不管左右空值全都进行连接,内连接:将左右两个表中都有的进行匹配连接,左连接:尽可能保存左边,右连接:尽可能保存右边。
on:表示用于连接的列名。相当于主码。
suffixes:表示修改重复列名。
sort:表示合并后会对数据进行排序。
- concat():沿一条轴将多个对象堆叠在一起。
- combine_first():主要用于处理 DataFrame 或 Series 中的缺失值(NaN)。结合两个DataFrame,以调用者的DataFrame为基准,用另一个DataFrame中的非NA/null值来填充它。
import pandas as pd # 创建两个包含 NaN 的 DataFrame df1 = pd.DataFrame({ 'A': [1, 2, np.nan, 4], 'B': [5, np.nan, np.nan, 8], 'C': [9, 10, 11, np.nan] }) df2 = pd.DataFrame({ 'A': [np.nan, np.nan, 3, np.nan], 'B': [np.nan, 6, np.nan, np.nan], 'C': [np.nan, np.nan, np.nan, 12] }) # 使用 combine_first 来填充 df1 中的 NaN 值 # 注意:combine_first 默认会优先使用调用它的对象(即 df1)中的值 df_combined = df1.combine_first(df2) print(df_combined)
A B C 0 1.0 5.0 9.0 1 2.0 6.0 10.0 2 3.0 NaN 11.0 3 4.0 8.0 12.0
在
df1
中的 NaN 值被df2
中相应位置的值所替代(如果df2
中有值的话)。