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.538892961.53889296 (大于1,减去1)
  • 原始值 -1.16823837abs(-1.16823837) = 1.168238371.16823837 - 1 = 0.16823837 (小于0且绝对值大于1)
  • 原始值 -0.195805770.19580577 (小于0但绝对值不大于1)
  • 原始值 0.765228660.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函数以获得最佳性能