自己再写一遍的pandas习题,相比于标准答案添加了自己的理解与注释,也可直接下载链接上的原习题(未加注释的初始版带答案)
链接:https://pan.baidu.com/s/1arrqcBFZKqJngzRzUB2QfA?pwd=29eb
提取码:29eb
–来自百度网盘超级会员V3的分享
import pandas as pd
import numpy as np
pd.__version__,np.__version__
('1.4.2', '1.22.4')
# 备注:使用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 |
# 备注:使用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 |
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类型对象。这一点知道就好,实际使用中影响不大
# 备注:使用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 |
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 |
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 |
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])
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 |
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
# 合并数据
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
# 合并数据
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
df[df['col1']%5 == 0].index.to_list()
[8]
np.argwhere 是一个非常有用的 NumPy 函数,它返回输入数组中满足特定条件的元素的索引。
它的返回值是一个多维数组,其中每一行代表满足条件的元素的索引。
np.argwhere(df['col1'].values%5==0).flatten().tolist()
[8]
-从DataFrame ’ df ‘的’ column1 '中提取值作为NumPy数组。
-用5计算数组中每个值的模,并检查结果是否为0。这将产生一个布尔数组,其中每个’ True ‘值都对应于’ column1 '中的一个可以被5整除的元素。
-该函数接受布尔数组作为参数,并返回“True”值的索引。在此代码的上下文中,它将返回能被5整除的数字的位置。
-“np”.argwhere ‘函数以2D数组格式返回索引,即使输入是1D数组。要将这个二维的下标数组转换为一维数组,我们可以使用’ flatten() '方法。
-将NumPy数组转换为Python列表。
简单地说,这行代码查找’ column1 '中能被5整除的数字的位置
这是pandas的方法,主要用于将pandas对象(如 Series 和 DataFrame 的某一列)转换为标准的Python列表。
这是NumPy的方法,用于将numpy.ndarray对象转换为Python列表。
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
注:标准答案里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 |
df.iloc[[1,10,15],0]
1 82
10 13
15 68
Name: col1, dtype: int32
# 备注:即比它前一个与后一个数字的都大的数字
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]
可以使用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]
在 Pandas 中,元素级(element-wise)操作通常是对 DataFrame 或 Series 中的每一个元素独立地进行的操作。这与聚合操作(如求和、平均等)或整体操作(如排序、删除列等)相反。
以下几点有助于你判断是否是进行元素级的操作:
返回值的形状
元素级操作通常返回与输入相同形状的对象。例如,如果你对一个 DataFrame 的某一列应用一个数学函数(如平方根),返回的结果也会是一个具有相同行数的 Series。
操作的独立性
如果每个元素的处理不依赖于其他元素(即独立地应用于每个元素),那么这通常是一个元素级操作。
使用的函数或操作符
一些 Pandas 的函数或方法明确地表示它们进行的是元素级操作。例如,apply()
方法可以用于 DataFrame 和 Series,以元素级方式应用一个函数。
比较和逻辑操作
当你使用比较运算符(如 >
, <
, ==
等)或逻辑运算符(在 Pandas 中应使用按位运算符 &
和 |
)时,这些通常是元素级操作。
例如,在表达式 df['col1'] > df['col2']
中,Pandas 会逐一比较两列中的每一对元素,并返回一个与输入列具有相同形状的布尔 Series。
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” 列中的局部最大值。这是代码的工作方式:
np.diff(df['col1'])
计算列 “col1” 中相邻元素的差值。np.sign(np.diff(df['col1']))
计算这些差值的符号(-1, 0, 1)。np.diff(np.sign(np.diff(df['col1'])))
计算这些符号的差值。np.where(tem == -2)[0] + 1
找到符号差值为 -2 的位置。符号差值为 -2 表示一个局部最大值(从上升趋势到下降趋势)。
这是一种非常巧妙和高效的方法!它利用了 NumPy 的向量化运算,因此通常会比纯 Python 循环更快。
这种方法的优点是它非常高效,特别是当你有大量数据时。它也比使用循环更简洁。
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
# 备注:每次移动三个位置,不可以使用自定义函数
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,即每次计算平均值时都会考虑当前元素及其前面的两个元素(如果有的话)。所以,在这个设置下,第一个和第二个元素没有足够的前面的元素来形成一个完整的窗口(大小为 3),因此其移动平均值是 NaN
。从第三个元素开始,每个元素都有一个大小为 3 的窗口,因此我们可以计算其移动平均值。
moving_average_col2_step2 = df[‘col2’].rolling(window=3, min_periods=1).mean()[::2]
moving_average_col2_step2
你也会注意到我使用了 [::2],这是 Python 的切片语法,用于从结果中选取每隔一个元素。这样,我们就得到了一个步长为 2 的移动平均值序列。
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。
这种方法非常高效,特别是当你有大量数据时。它也比使用循环更简洁。
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 |
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 |
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=1∑n(Ai−Bi)2
在 Pandas DataFrame 中,你可以很容易地计算两列之间的欧式距离。
下面是如何计算 DataFrame 中 “col2” 和 “col3” 列之间的欧式距离:
“col2” 和 “col3” 列之间的欧式距离是 3.0 3.0 3.0。
这是通过计算两列中对应元素的差值的平方,然后求和,最后取平方根得到的。
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=1∑nxi2
在这个特定的情境下, x 是 “col2” 和 “col3” 列之间的差值。
这种方法非常高效和精确,特别是当你有大量数据时。