Python数据处理与变换对话记录
目录
问题描述
用户有一个数组 data1,需要进行以下变换:
1. 对于大于1的元素:减去1
2. 对于小于0的元素:取绝对值
3. 如果绝对值大于1:再减去1
原始数据:
data1 = [-0.30723328, 0.76522866, -1.16823837, -0.19580577, -1.02876476, -1.90951602,
-1.92152706, -0.54222187, -1.00005355, -0.35720395, -1.09054057, -0.57282588,
0.01674342, -0.28207566, 0.48320257, -0.73044074, 2.53889296, 1.05645781,
-0.27734719, -0.98336046, -0.49144772, 0.8104907, 1.19454848, 0.30489613,
0.7801276, 0.63072873, 0.92062873, -2.55239969, 0.51426178, -0.5688309,
-0.45569837, -0.54926006, -1.63270893, 0.16333685, -0.38627737, 1.59708009,
0.85802794, 0.97584194, -1.31239538, -0.55416648, -0.76932212, -1.0331699,
-0.13146155, -0.31994977, 0.17220971, -1.64730348, 0.51763084, 0.38742204,
0.36295653, -0.27065254, 0.26083338, 0.66042526, -0.1641674, -0.4717715,
0.65728938, 0.20425213, -0.17153585, 1.68276713, 0.88600606, 2.37514264,
-0.18682816, -1.31804506, -0.92972951, 0.13314904, -0.89322317, 1.42677902,
0.19375654, 0.36058322, -1.32249329, 0.63251623, -0.43339076, 1.12679103,
-1.32733559, 0.60865354, 0.42927908, 0.18752629, -1.02989332, 0.64531559,
-0.39493969, 0.1812403, 0.31624433, 0.28587404, 1.25751838, -2.58872045,
0.89158749, -1.16373075, -0.07248178, -1.67455235, 0.45307442, -2.22437815,
0.33878087, -0.57765634, -0.55265427, -2.26559099, -0.09573752, 0.49828709,
1.02830406, 0.35409416, -0.54465344, 1.61011631]
数据变换解决方案
方案1:使用NumPy向量化操作
import numpy as np
# 创建结果的副本
result = data1.copy()
# 处理大于1的元素:减去1
result[result > 1] = result[result > 1] - 1
# 处理小于0的元素
negative_mask = result < 0
negative_values = result[negative_mask]
# 对小于0的元素取绝对值
abs_values = np.abs(negative_values)
# 对绝对值大于1的再减去1
abs_values[abs_values > 1] = abs_values[abs_values > 1] - 1
# 将处理后的值放回原数组
result[negative_mask] = abs_values
方案2:使用自定义函数和np.vectorize
def transform_value(x):
if x > 1:
return x - 1
elif x < 0:
abs_x = abs(x)
return abs_x - 1 if abs_x > 1 else abs_x
else:
return x
# 使用向量化函数
vectorized_transform = np.vectorize(transform_value)
result = vectorized_transform(data1)
变换规则示例
- 原始值
2.53889296→1.53889296(大于1,减去1) - 原始值
-1.16823837→abs(-1.16823837) = 1.16823837→1.16823837 - 1 = 0.16823837(小于0且绝对值大于1) - 原始值
-0.19580577→0.19580577(小于0但绝对值不大于1) - 原始值
0.76522866→0.76522866(在0-1之间,保持不变)
Python数据取整方法
1. 基本取整函数
import math
import numpy as np
# 示例数据
numbers = [3.2, 3.7, -3.2, -3.7, 2.5, 3.5]
# round() - 四舍五入
print([round(x) for x in numbers]) # [3, 4, -3, -4, 2, 4]
# int() - 向零取整(截断小数)
print([int(x) for x in numbers]) # [3, 3, -3, -3, 2, 3]
# math.floor() - 向下取整
print([math.floor(x) for x in numbers]) # [3, 3, -4, -4, 2, 3]
# math.ceil() - 向上取整
print([math.ceil(x) for x in numbers]) # [4, 4, -3, -3, 3, 4]
# math.trunc() - 向零取整
print([math.trunc(x) for x in numbers]) # [3, 3, -3, -3, 2, 3]
2. NumPy数组取整
# 创建示例数组
arr = np.array([1.2, 2.7, -1.8, -2.3, 3.5, 4.6])
# np.round() - 四舍五入
print(np.round(arr)) # [ 1. 3. -2. -2. 4. 5.]
# np.floor() - 向下取整
print(np.floor(arr)) # [ 1. 2. -2. -3. 3. 4.]
# np.ceil() - 向上取整
print(np.ceil(arr)) # [ 2. 3. -1. -2. 4. 5.]
# np.trunc() - 向零取整
print(np.trunc(arr)) # [ 1. 2. -1. -2. 3. 4.]
# 指定小数位数
arr_decimal = np.array([1.23456, 2.78901, 3.14159])
print(np.round(arr_decimal, 2)) # [1.23 2.79 3.14]
3. 指定小数位数的取整
arr = np.array([1.23456, 2.78901, 3.14159, 4.56789])
# 保留不同位数小数
print(np.round(arr, 0)) # [1. 3. 3. 5.]
print(np.round(arr, 1)) # [1.2 2.8 3.1 4.6]
print(np.round(arr, 2)) # [1.23 2.79 3.14 4.57]
print(np.round(arr, 3)) # [1.235 2.789 3.142 4.568]
# 使用格式化显示
for x in arr:
print(f"{x:.2f}") # 保留2位小数
4. 银行家舍入法
def bankers_round(x, decimals=0):
"""银行家舍入法:四舍六入五成双"""
multiplier = 10 ** decimals
x_multiplied = x * multiplier
# 判断最后一位是否为5
if abs(x_multiplied - round(x_multiplied)) == 0.5:
# 五成双:如果前一位是偶数则舍,奇数则入
if int(x_multiplied) % 2 == 0:
return math.floor(x_multiplied) / multiplier
else:
return math.ceil(x_multiplied) / multiplier
else:
return round(x, decimals)
numpy.vectorize函数详解
基本概念
numpy.vectorize 可以将普通的Python函数"向量化",使其能够处理numpy数组并享受向量化操作的好处。注意:它并不是真正的性能优化(本质上还是循环),而是提供了numpy数组的广播机制和便利性。
基本语法
numpy.vectorize(pyfunc, otypes=None, doc=None, excluded=None, cache=False, signature=None)
简单示例
import numpy as np
# 定义一个普通的Python函数
def my_func(x):
if x > 0:
return x ** 2
else:
return x ** 3
# 使用vectorize创建向量化版本
vectorized_func = np.vectorize(my_func)
# 创建测试数组
arr = np.array([1, -2, 3, -4, 5])
# 应用向量化函数
result = vectorized_func(arr) # [1, -8, 9, -64, 25]
重要参数详解
1. otypes - 指定输出类型
def string_func(x):
return f"value_{x}"
# 指定输出类型为字符串
vfunc = np.vectorize(string_func, otypes=[str])
result = vfunc([1, 2, 3]) # ['value_1', 'value_2', 'value_3']
2. excluded - 排除某些参数不被向量化
def power_func(x, exponent=2):
return x ** exponent
# 创建向量化函数,排除exponent参数
vfunc = np.vectorize(power_func, excluded=['exponent'])
arr = np.array([1, 2, 3, 4, 5])
result1 = vfunc(arr, exponent=2) # [1, 4, 9, 16, 25]
result2 = vfunc(arr, exponent=3) # [1, 8, 27, 64, 125]
3. 多参数函数
def multi_param_func(x, y, z):
return x * y + z
# 向量化多参数函数
vfunc = np.vectorize(multi_param_func)
# 不同形状的数组(广播机制)
arr1 = np.array([1, 2, 3])
arr2 = np.array([[1], [2], [3]])
scalar = 10
result = vfunc(arr1, arr2, scalar)
性能注意事项
import numpy as np
import time
def simple_math(x):
return x ** 2 + np.sin(x)
# 创建测试数据
large_data = np.random.randn(100000)
# 方法1: vectorize
vfunc = np.vectorize(simple_math)
start = time.time()
result1 = vfunc(large_data)
time1 = time.time() - start
# 方法2: 原生numpy向量化(推荐)
start = time.time()
result2 = large_data ** 2 + np.sin(large_data)
time2 = time.time() - start
print(f"vectorize 耗时: {time1:.4f}秒")
print(f"原生numpy耗时: {time2:.4f}秒")
print(f"速度比: {time1/time2:.1f}x")
科学计数法显示问题解决
问题描述
用户使用以下代码时,输出结果使用了科学计数法,与预期不符:
import math
def transform_value(x):
temp=round(float(x),4)
return abs(temp)-int(abs(temp))
# 使用向量化函数
vectorized_transform = np.vectorize(transform_value)
print(vectorized_transform(data2))
输出结果(部分):
[3.072e-01 7.652e-01 1.682e-01 1.958e-01 2.880e-02 ...]
问题分析
代码逻辑正确,但输出使用了科学计数法显示很小的数值,导致看起来与预期不符。
解决方案
方案1:设置numpy显示选项(推荐)
import numpy as np
# 设置numpy不使用科学计数法,显示4位小数
np.set_printoptions(precision=4, suppress=True)
def transform_value(x):
temp = round(float(x), 4)
return abs(temp) - int(abs(temp))
vectorized_transform = np.vectorize(transform_value)
result = vectorized_transform(data2)
print(result)
方案2:手动格式化输出
def transform_value(x):
temp = round(float(x), 4)
return abs(temp) - int(abs(temp))
vectorized_transform = np.vectorize(transform_value)
result = vectorized_transform(data2)
# 格式化每个元素
formatted_result = [f"{x:.4f}" for x in result]
print(formatted_result)
方案3:在函数内部处理精度
def transform_value(x):
temp = round(float(x), 4)
result = abs(temp) - int(abs(temp))
return round(result, 4) # 对最终结果也保留4位小数
vectorized_transform = np.vectorize(transform_value)
result = vectorized_transform(data2)
print(result)
验证计算过程
# 测试几个值
test_values = [-0.30723328, -1.16823837, 2.53889296]
for val in test_values:
temp = round(float(val), 4) # 四舍五入到4位
abs_temp = abs(temp) # 取绝对值
integer_part = int(abs_temp) # 整数部分
fractional_part = abs_temp - integer_part # 小数部分
print(f"原始值: {val:.8f}")
print(f"四舍五入: {temp:.4f}")
print(f"绝对值: {abs_temp:.4f}")
print(f"整数部分: {integer_part}")
print(f"小数部分: {fractional_part:.4f}")
print("---")
输出:
原始值: -0.30723328
四舍五入: -0.3072
绝对值: 0.3072
整数部分: 0
小数部分: 0.3072
原始值: -1.16823837
四舍五入: -1.1682
绝对值: 1.1682
整数部分: 1
小数部分: 0.1682
原始值: 2.53889296
四舍五入: 2.5389
绝对值: 2.5389
整数部分: 2
小数部分: 0.5389
总结
本文档记录了从数据变换需求到最终解决问题的完整过程,包括: 1. 数据变换的逻辑和实现 2. Python中各种取整方法 3. numpy.vectorize函数的详细用法 4. 科学计数法显示问题的解决方案
关键要点:
- 使用NumPy向量化操作可以提高代码效率和简洁性
- numpy.vectorize适用于复杂逻辑的函数向量化,但不是性能优化的首选
- 处理小数显示问题时,使用np.set_printoptions()可以控制显示格式
- 对于数值计算,优先使用原生NumPy函数以获得最佳性能