Pandas进阶修炼120题-第四期(当Pandas遇上NumPy,81-100题)

目录

    • 往期内容:
    • 第一期:Pandas基础(1-20题)
    • 第二期:Pandas数据处理(21-50题)
    • 第三期:Pandas金融数据处理(51-80题)
    • 第四期:当Pandas遇上NumPy(81-100题)
    • 第四期 当Pandas遇上NumPy
      • 81.导入并查看pandas与numpy版本
      • 82.从NumPy数组创建DataFrame
      • 83.从NumPy数组创建DataFrame
          • range 和 arange 的区别
      • 84.从NumPy数组创建DataFrame
      • 85.将df1,df2,df3按照行合并为新DataFrame
      • 86.将df1,df2,df3按照列合并为新DataFrame
      • 87.查看df所有数据的最小值、25%分位数、中位数、75%分位数、最大值
      • 88.修改列名为col1,col2,col3
      • 89.提取第一列中不在第二列出现的数字
      • 90.提取第一列和第二列出现频率最高的三个数字
          • 方法一 :value_connts().head(3)
          • 方法二:value_connts()[:3]
      • 91.提取第一列中可以整除5的数字位置
          • 方法一 :.index
          • 方法二 :np.argwhere()
          • 拓展:to_list() 和 tolist()区别与联系
      • 92.计算第一列数字前一个与后一个的差值
      • 93.将col1,col2,clo3三列顺序颠倒
      • 94.提取第一列位置在1,10,15的数字
      • 95.查找第一列的局部最大值位置
          • 方法一:写逻辑判断局部最大值的位置
          • 方法二:shift方法实现比较
          • 方法二拓展:上述示例中如果涉及元素级别的比较 是不可以使用and的,如果不涉及元素比较我们最好使用and连接,详见方法一优化
          • 方法三(标准答案解析):利用了 NumPy 的向量化运算
      • 96.按行计算df的每一行均值
      • 97.对第二列计算移动平均值
          • 方法一:rolling方法
          • rolling() 设置步长的方法:
          • 方法二:标准答案 convolve()方法
      • 98.将数据按照第三列值的大小升序排列
      • 99.将第一列大于50的数字修改为'高'
      • 100.计算第二列与第三列之间的欧式距离
          • 方法一:通过计算两列中对应元素的差值的平方,然后求和,最后取平方根得到的。
          • 方法二:linalg.norm

自己再写一遍的pandas习题,相比于标准答案添加了自己的理解与注释,也可直接下载链接上的原习题(未加注释的初始版带答案)
链接:https://pan.baidu.com/s/1arrqcBFZKqJngzRzUB2QfA?pwd=29eb
提取码:29eb
–来自百度网盘超级会员V3的分享

往期内容:

  • 第一期:Pandas基础(1-20题)

  • 第二期:Pandas数据处理(21-50题)

  • 第三期:Pandas金融数据处理(51-80题)

  • 第四期:当Pandas遇上NumPy(81-100题)

第四期 当Pandas遇上NumPy

81.导入并查看pandas与numpy版本

import pandas as pd
import numpy as np
pd.__version__,np.__version__
('1.4.2', '1.22.4')

82.从NumPy数组创建DataFrame

# 备注:使用numpy生成20个0-100随机数
data = np.random.randint(0,100,20)
df1 = pd.DataFrame(data = data)
df1
0
0 63
1 82
2 3
3 26
4 47
5 34
6 72
7 46
8 75
9 69
10 13
11 67
12 84
13 94
14 27
15 68
16 14
17 51
18 38
19 31

83.从NumPy数组创建DataFrame

# 备注:使用numpy生成20个0-100固定步长的数
data = np.arange(0,100,5)
df2 = pd.DataFrame(data = data)
df2
0
0 0
1 5
2 10
3 15
4 20
5 25
6 30
7 35
8 40
9 45
10 50
11 55
12 60
13 65
14 70
15 75
16 80
17 85
18 90
19 95
range 和 arange 的区别

1:range() 是Python内置的函数,直接调用即可;arange() 是numpy中的函数,调用时一般采用np.arange()。

2: range()函数仅支持整数步长

我们知道,range()函数具有3个参数,分别是起点、终点和步长。需要注意的是这3个参数均为整数型,不能设置为浮点型,这也导致了range()函数的返回值同样都是整数型的。

3: .np.arange()函数支持小数步长

当然,在项目开过程中难免会遇到需要小数迭代序列,这时就需要np.arange()函数发挥它的作用了!首先,需要明确range()函数对整数参数的使用,np.arange()函数都能替代。

4:range()函数返回的类型为range对象,np.arange()函数返回的类型为array类型对象。这一点知道就好,实际使用中影响不大

84.从NumPy数组创建DataFrame

# 备注:使用numpy生成20个指定分布(如标准正态分布)的数
data = np.random.normal(size = 20)
df3 = pd.DataFrame(data = data)
df3
0
0 -1.832852
1 0.309764
2 -0.607917
3 -2.671872
4 -0.576923
5 -1.306861
6 -1.335706
7 0.387466
8 -0.013463
9 0.688958
10 -0.683797
11 -0.893810
12 -0.494232
13 0.239309
14 0.080451
15 -0.975732
16 -1.267988
17 -0.072968
18 0.650972
19 -0.429822

85.将df1,df2,df3按照行合并为新DataFrame

df = pd.concat([df1,df2,df3],axis = 0,ignore_index = True)
df
0
0 63.000000
1 82.000000
2 3.000000
3 26.000000
4 47.000000
5 34.000000
6 72.000000
7 46.000000
8 75.000000
9 69.000000
10 13.000000
11 67.000000
12 84.000000
13 94.000000
14 27.000000
15 68.000000
16 14.000000
17 51.000000
18 38.000000
19 31.000000
20 0.000000
21 5.000000
22 10.000000
23 15.000000
24 20.000000
25 25.000000
26 30.000000
27 35.000000
28 40.000000
29 45.000000
30 50.000000
31 55.000000
32 60.000000
33 65.000000
34 70.000000
35 75.000000
36 80.000000
37 85.000000
38 90.000000
39 95.000000
40 -1.832852
41 0.309764
42 -0.607917
43 -2.671872
44 -0.576923
45 -1.306861
46 -1.335706
47 0.387466
48 -0.013463
49 0.688958
50 -0.683797
51 -0.893810
52 -0.494232
53 0.239309
54 0.080451
55 -0.975732
56 -1.267988
57 -0.072968
58 0.650972
59 -0.429822

86.将df1,df2,df3按照列合并为新DataFrame

df = pd.concat([df1,df2,df3],axis = 1,ignore_index = True)
df
0 1 2
0 63 0 -1.832852
1 82 5 0.309764
2 3 10 -0.607917
3 26 15 -2.671872
4 47 20 -0.576923
5 34 25 -1.306861
6 72 30 -1.335706
7 46 35 0.387466
8 75 40 -0.013463
9 69 45 0.688958
10 13 50 -0.683797
11 67 55 -0.893810
12 84 60 -0.494232
13 94 65 0.239309
14 27 70 0.080451
15 68 75 -0.975732
16 14 80 -1.267988
17 51 85 -0.072968
18 38 90 0.650972
19 31 95 -0.429822

87.查看df所有数据的最小值、25%分位数、中位数、75%分位数、最大值

np.percentile(df,q = [0,25,50,75,100])
array([-2.67187243e+00,  6.03382589e-02,  2.65000000e+01,  6.35000000e+01,
        9.50000000e+01])

88.修改列名为col1,col2,col3

df.columns = ['col1','col2','col3']
df.head()
col1 col2 col3
0 63 0 -1.832852
1 82 5 0.309764
2 3 10 -0.607917
3 26 15 -2.671872
4 47 20 -0.576923

89.提取第一列中不在第二列出现的数字

mask = ~df['col1'].isin(df['col2']) # 结果Serise,符合条件为True,否则为False
df['col1'][mask]
0     63
1     82
2      3
3     26
4     47
5     34
6     72
7     46
9     69
10    13
11    67
12    84
13    94
14    27
15    68
16    14
17    51
18    38
19    31
Name: col1, dtype: int32

90.提取第一列和第二列出现频率最高的三个数字

方法一 :value_connts().head(3)
# 合并数据
combined_series = pd.concat([df['col1'],df['col2']],axis = 0,ignore_index = True)
# 获取频率最高的三个数字
top_three_nunbers = combined_series.value_counts().head(3)
top_three_nunbers
75    2
63    1
45    1
dtype: int64
方法二:value_connts()[:3]
# 合并数据
combined_series = pd.concat([df['col1'],df['col2']],axis = 0,ignore_index = True)
# 获取频率最高的三个数字
top_three_nunbers = combined_series.value_counts()[:3]
top_three_nunbers
75    2
63    1
45    1
dtype: int64

91.提取第一列中可以整除5的数字位置

方法一 :.index
df[df['col1']%5 == 0].index.to_list()
[8]
方法二 :np.argwhere()

np.argwhere 是一个非常有用的 NumPy 函数,它返回输入数组中满足特定条件的元素的索引。

它的返回值是一个多维数组,其中每一行代表满足条件的元素的索引。

np.argwhere(df['col1'].values%5==0).flatten().tolist()
[8]
  1. df(“col1”).values

-从DataFrame ’ df ‘的’ column1 '中提取值作为NumPy数组。

  1. 的df(“column1”).值% 5 == 0

-用5计算数组中每个值的模,并检查结果是否为0。这将产生一个布尔数组,其中每个’ True ‘值都对应于’ column1 '中的一个可以被5整除的元素。

  1. np.argwhere(…):

-该函数接受布尔数组作为参数,并返回“True”值的索引。在此代码的上下文中,它将返回能被5整除的数字的位置。

  1. .flatten()的

-“np”.argwhere ‘函数以2D数组格式返回索引,即使输入是1D数组。要将这个二维的下标数组转换为一维数组,我们可以使用’ flatten() '方法。

  1. 的.tolist()的

-将NumPy数组转换为Python列表。

简单地说,这行代码查找’ column1 '中能被5整除的数字的位置

拓展:to_list() 和 tolist()区别与联系
  • to_list():

这是pandas的方法,主要用于将pandas对象(如 Series 和 DataFrame 的某一列)转换为标准的Python列表。

  • tolist():

这是NumPy的方法,用于将numpy.ndarray对象转换为Python列表。

92.计算第一列数字前一个与后一个的差值

df['col1'].diff()
0      NaN
1     19.0
2    -79.0
3     23.0
4     21.0
5    -13.0
6     38.0
7    -26.0
8     29.0
9     -6.0
10   -56.0
11    54.0
12    17.0
13    10.0
14   -67.0
15    41.0
16   -54.0
17    37.0
18   -13.0
19    -7.0
Name: col1, dtype: float64

93.将col1,col2,clo3三列顺序颠倒

注:标准答案里df.ix[:,::-1]也可以解决问题,但是ix将会在后续的语法更新中废除,所以不建议使用

df.iloc[:,::-1]
col3 col2 col1
0 -1.832852 0 63
1 0.309764 5 82
2 -0.607917 10 3
3 -2.671872 15 26
4 -0.576923 20 47
5 -1.306861 25 34
6 -1.335706 30 72
7 0.387466 35 46
8 -0.013463 40 75
9 0.688958 45 69
10 -0.683797 50 13
11 -0.893810 55 67
12 -0.494232 60 84
13 0.239309 65 94
14 0.080451 70 27
15 -0.975732 75 68
16 -1.267988 80 14
17 -0.072968 85 51
18 0.650972 90 38
19 -0.429822 95 31

94.提取第一列位置在1,10,15的数字

df.iloc[[1,10,15],0]
1     82
10    13
15    68
Name: col1, dtype: int32

95.查找第一列的局部最大值位置

方法一:写逻辑判断局部最大值的位置
# 备注:即比它前一个与后一个数字的都大的数字
length = len(df)
res = []
for i in range(1,length-1): # 注意这里循环的起止点为第2个数字到倒数第二个数字,保证数据的前后都有数据
    if (df['col1'][i] > df['col1'][i-1]) & (df['col1'][i] > df['col1'][i+1]):
        res.append(i)
res
[1, 4, 6, 8, 13, 15, 17]
##### 方法一修正

方法一虽然很好的的解决了问题,但是GPT还是针对上述代码提出了意见:主要是考虑边界条件和尽可能使用条件运算符而不是位运算符

1. 可读性和可维护性
对于简短的代码段,这可能不是一个大问题,但对于更大的代码库,使用具有描述性名称的变量和添加注释是一种好习惯。

2. 边界条件
你的代码没有考虑列表的第一个和最后一个元素。根据定义,如果第一个(或最后一个)元素大于其唯一的相邻元素,那么它也是一个局部最大值。

3. 使用逻辑运算符
在 Python 中,逻辑运算(如 and, or)通常用于布尔表达式,而不是位运算符(如 &, |)。

4. 使用 Pandas 的内置函数
对于这种类型的操作,Pandas 提供了一些内置函数,如 shift(),它可以更简洁地完成相同的任务。
# 代码优化
length = len(df)
res_enhanced = []

# 考虑第一个元素
if df['col1'][0] > df['col1'][1]:
    res_enhanced.append(0)

# 考虑中间元素
for i in range(1,length - 1):
    if (df['col1'][i] > df['col1'][i-1]) and (df['col1'][i] > df['col1'][i+1]):
        res_enhanced.append(i)

# 考虑最后一个元素
if df['col1'][length - 1] > df['col1'][length - 2]:
    res_enhanced.append(length - 1)

res_enhanced
[1, 4, 6, 8, 13, 15, 17]
方法二:shift方法实现比较

可以使用pandas库中的 shift 方法来对数据进行滞后和提前操作。

具体步骤如下:

1:使用 shift(1) 得到前一个值。

2:使用 shift(-1) 得到后一个值。

3:对这三个值进行比较,确定当前值是否为局部最大值。

# 使用pandas 的 shift()方法
df['prev_col1'] = df['col1'].shift(1) # 前一个元素
df['next_col1'] = df['col1'].shift(-1) # 后一个元素

# 查找局部最大值
# local_maxima_with_shift = df[(df['col1'] > df['prev_col1']) and (df['col1'] > df['next_col1'])].index.tolist()
local_maxima_with_shift = df[(df['col1'] > df['prev_col1']) & (df['col1'] > df['next_col1'])].index.tolist()

# 删除添加的列
df.drop(['prev_col1','next_col1'],axis = 1,inplace = True)

local_maxima_with_shift
[1, 4, 6, 8, 13, 15, 17]
方法二拓展:上述示例中如果涉及元素级别的比较 是不可以使用and的,如果不涉及元素比较我们最好使用and连接,详见方法一优化

在 Pandas 中,元素级(element-wise)操作通常是对 DataFrame 或 Series 中的每一个元素独立地进行的操作。这与聚合操作(如求和、平均等)或整体操作(如排序、删除列等)相反。

以下几点有助于你判断是否是进行元素级的操作:

  1. 返回值的形状
    元素级操作通常返回与输入相同形状的对象。例如,如果你对一个 DataFrame 的某一列应用一个数学函数(如平方根),返回的结果也会是一个具有相同行数的 Series。

  2. 操作的独立性
    如果每个元素的处理不依赖于其他元素(即独立地应用于每个元素),那么这通常是一个元素级操作。

  3. 使用的函数或操作符
    一些 Pandas 的函数或方法明确地表示它们进行的是元素级操作。例如,apply() 方法可以用于 DataFrame 和 Series,以元素级方式应用一个函数。

  4. 比较和逻辑操作
    当你使用比较运算符(如 >, <, == 等)或逻辑运算符(在 Pandas 中应使用按位运算符 &|)时,这些通常是元素级操作。

例如,在表达式 df['col1'] > df['col2'] 中,Pandas 会逐一比较两列中的每一对元素,并返回一个与输入列具有相同形状的布尔 Series。

  1. 无副作用
    元素级操作通常没有副作用,即它们不会改变输入对象的状态。相反,它们通常返回一个新的对象。
方法三(标准答案解析):利用了 NumPy 的向量化运算
tem = np.diff(np.sign(np.diff(df['col1'])))
np.where(tem == -2)[0] + 1
array([ 1,  4,  6,  8, 13, 15, 17], dtype=int64)

答案解析:

这段代码使用 NumPy 库来找到 “col1” 列中的局部最大值。这是代码的工作方式:

  1. np.diff(df['col1']) 计算列 “col1” 中相邻元素的差值。
  2. np.sign(np.diff(df['col1'])) 计算这些差值的符号(-1, 0, 1)。
  3. np.diff(np.sign(np.diff(df['col1']))) 计算这些符号的差值。
  4. np.where(tem == -2)[0] + 1 找到符号差值为 -2 的位置。

符号差值为 -2 表示一个局部最大值(从上升趋势到下降趋势)。

这是一种非常巧妙和高效的方法!它利用了 NumPy 的向量化运算,因此通常会比纯 Python 循环更快。

这种方法的优点是它非常高效,特别是当你有大量数据时。它也比使用循环更简洁。

96.按行计算df的每一行均值

df[['col1', 'col2', 'col3']].mean(axis=1)
0     20.389049
1     29.103255
2      4.130694
3     12.776043
4     22.141026
5     19.231046
6     33.554765
7     27.129155
8     38.328846
9     38.229653
10    20.772068
11    40.368730
12    47.835256
13    53.079770
14    32.360150
15    47.341423
16    30.910671
17    45.309011
18    42.883657
19    41.856726
dtype: float64

97.对第二列计算移动平均值

方法一:rolling方法
# 备注:每次移动三个位置,不可以使用自定义函数
df['col2'].rolling(window = 3).mean()
0      NaN
1      NaN
2      5.0
3     10.0
4     15.0
5     20.0
6     25.0
7     30.0
8     35.0
9     40.0
10    45.0
11    50.0
12    55.0
13    60.0
14    65.0
15    70.0
16    75.0
17    80.0
18    85.0
19    90.0
Name: col2, dtype: float64

代码解析:

window = 3的含义是窗口大小等于3,移动步长为1是吧

  • window=3 表示窗口大小是 3,即每次计算平均值时都会考虑当前元素及其前面的两个元素(如果有的话)。
  • 默认情况下,移动步长(也称为步幅或偏移量)是 1,这意味着窗口会从第一个元素开始,每次向后移动一个位置,直到达到序列的末尾。

所以,在这个设置下,第一个和第二个元素没有足够的前面的元素来形成一个完整的窗口(大小为 3),因此其移动平均值是 NaN。从第三个元素开始,每个元素都有一个大小为 3 的窗口,因此我们可以计算其移动平均值。

rolling() 设置步长的方法:
  • 计算第二列(“col2”)的移动平均值,窗口大小为 3,步长为 2
  • 注意:这个功能在 Pandas 1.2.0 及更高版本中可用

moving_average_col2_step2 = df[‘col2’].rolling(window=3, min_periods=1).mean()[::2]

moving_average_col2_step2

你也会注意到我使用了 [::2],这是 Python 的切片语法,用于从结果中选取每隔一个元素。这样,我们就得到了一个步长为 2 的移动平均值序列。

方法二:标准答案 convolve()方法
np.convolve(df['col2'], np.ones(3)/3, mode='valid')
array([ 5., 10., 15., 20., 25., 30., 35., 40., 45., 50., 55., 60., 65.,
       70., 75., 80., 85., 90.])

代码解释如下:

  • df['col2'] 是我们要计算移动平均值的数据。
  • np.ones(3)/3 创建一个包含三个 1/3 的数组,这将用作卷积核。这等效于取窗口中所有元素的平均值。
  • mode='valid' 表示卷积操作不会超出输入数组的边界。因此,输出数组的大小将小于输入数组。

这段代码会计算一个窗口大小为 3 的移动平均值,步长为 1。

下面是如何使用这段代码:

通过使用 np.convolve,我们得到了一个包含 “col2” 列移动平均值的 NumPy 数组。由于我们使用了 mode='valid',输出数组的大小(7)小于输入数组的大小(9)。

在这个特定的例子中,由于 “col2” 列的所有值都是 2,所以移动平均值也是 2。

这种方法非常高效,特别是当你有大量数据时。它也比使用循环更简洁。

98.将数据按照第三列值的大小升序排列

df.sort_values(by = ['col3'],ignore_index = True)
col1 col2 col3
0 26 15 -2.671872
1 63 0 -1.832852
2 72 30 -1.335706
3 34 25 -1.306861
4 14 80 -1.267988
5 68 75 -0.975732
6 67 55 -0.893810
7 13 50 -0.683797
8 3 10 -0.607917
9 47 20 -0.576923
10 84 60 -0.494232
11 31 95 -0.429822
12 51 85 -0.072968
13 75 40 -0.013463
14 27 70 0.080451
15 94 65 0.239309
16 82 5 0.309764
17 46 35 0.387466
18 38 90 0.650972
19 69 45 0.688958

99.将第一列大于50的数字修改为’高’

df['col1'][df['col1']>50] = '高'
C:\Users\chengyuanting\AppData\Local\Temp\ipykernel_16460\101840694.py:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df['col1'][df['col1']>50] = '高'
df
col1 col2 col3
0 0 -1.832852
1 5 0.309764
2 3 10 -0.607917
3 26 15 -2.671872
4 47 20 -0.576923
5 34 25 -1.306861
6 30 -1.335706
7 46 35 0.387466
8 40 -0.013463
9 45 0.688958
10 13 50 -0.683797
11 55 -0.893810
12 60 -0.494232
13 65 0.239309
14 27 70 0.080451
15 75 -0.975732
16 14 80 -1.267988
17 85 -0.072968
18 38 90 0.650972
19 31 95 -0.429822

100.计算第二列与第三列之间的欧式距离

方法一:通过计算两列中对应元素的差值的平方,然后求和,最后取平方根得到的。
import math
d = math.sqrt(((df['col2']-df['col3'])**2).sum())
d
249.89929210175958

欧式距离(Euclidean distance)是在欧几里得空间中两点之间的 “直线” 距离。对于两个向量 A \mathbf{A} A B \mathbf{B} B,其欧氏距离计算公式为:

d ( A , B ) = ∑ i = 1 n ( A i − B i ) 2 d(\mathbf{A}, \mathbf{B}) = \sqrt{\sum_{i=1}^{n} (A_i - B_i)^2} d(A,B)=i=1n(AiBi)2

在 Pandas DataFrame 中,你可以很容易地计算两列之间的欧式距离。

下面是如何计算 DataFrame 中 “col2” 和 “col3” 列之间的欧式距离:

“col2” 和 “col3” 列之间的欧式距离是 3.0 3.0 3.0

这是通过计算两列中对应元素的差值的平方,然后求和,最后取平方根得到的。

方法二:linalg.norm
np.linalg.norm(df['col2']-df['col3'])
249.89929210175958

你提供的代码使用 NumPy 的 linalg.norm 函数来计算 “col2” 和 “col3” 列之间的欧式距离。这是一个非常高效和数学上精确的方法。

np.linalg.norm(x) 函数会计算向量 x 的 L2 范数(欧几里得范数),其公式与欧氏距离的计算公式相同:

norm ( x ) = ∑ i = 1 n x i 2 \text{norm}(x) = \sqrt{\sum_{i=1}^{n} x_i^2} norm(x)=i=1nxi2

在这个特定的情境下, x 是 “col2” 和 “col3” 列之间的差值。

这种方法非常高效和精确,特别是当你有大量数据时。

你可能感兴趣的:(pandas,numpy)