python copy

张开发
2026/4/5 0:04:05 15 分钟阅读

分享文章

python copy
# 聊聊Python里的unique在数据处理的时候经常会遇到需要找出列表中不重复元素的情况。比如统计用户访问记录里的独立访客或者分析商品销售数据里的不同品类。Python里处理这种需求有个很实用的方法叫unique。他是什么unique并不是Python内置的一个独立函数而是NumPy和pandas这些数据分析库里的功能。简单来说unique就是从一个序列比如列表、数组中提取出所有不重复的值并且按照一定的顺序排列返回。这有点像整理抽屉里的袜子把各种颜色款式的袜子都拿出来每种只留一只然后按颜色深浅排好。最后得到的就是一个没有重复的袜子集合。他能做什么unique的主要作用就是去重。但它的价值不仅仅在于去掉重复项更重要的是能够帮我们快速了解数据的构成。比如分析一家咖啡店一周的销售数据每天都有很多订单通过unique可以迅速知道这一周到底卖了多少种不同的饮品。或者处理用户注册信息时用unique可以检查邮箱地址是否有重复注册的情况。在数据清洗阶段unique经常用来检查数据的唯一性约束。如果某个理论上应该唯一的字段比如身份证号经过unique处理后数量变少了那就说明数据中存在重复记录需要进一步处理。怎么使用在pandas里使用unique很简单。假设有个DataFrame存储着学生的考试成绩importpandasaspd data{姓名:[张三,李四,王五,张三,赵六],科目:[数学,数学,英语,数学,英语],分数:[85,90,88,85,92]}dfpd.DataFrame(data)要查看有哪些不同的学生参加了考试可以这样unique_namesdf[姓名].unique()print(unique_names)输出会是[‘张三’ ‘李四’ ‘王五’ ‘赵六’]注意张三只出现了一次虽然数据里有两条张三的记录。在NumPy里用法也类似importnumpyasnp arrnp.array([1,2,2,3,3,3,4])unique_valuesnp.unique(arr)得到的结果是[1 2 3 4]。unique默认会返回排序后的结果这个特性有时候很有用但也要注意它消耗额外的计算资源。如果只是想去重而不关心顺序可以考虑用set不过set不保证顺序而且返回的是集合类型。最佳实践使用unique时有些细节值得注意。首先是数据类型的问题unique对数据类型比较敏感。字符串和数字混合的序列或者包含NaN值的情况处理起来需要小心。比如有这样一个数组[1, 2, ‘3’, 2, np.nan]直接调用unique可能会得到意想不到的结果。通常的做法是先做类型转换或者处理缺失值。另一个实践是结合value_counts一起使用。unique告诉你有哪些不同的值value_counts告诉每个值出现了多少次。这两个方法经常搭档出现能提供更完整的数据分布信息。# 查看每个学生参加了几门考试name_countsdf[姓名].value_counts()对于大数据集unique的性能表现很重要。pandas的unique实现得比较高效但如果是特别大的数据比如上亿条可能需要考虑分块处理或者使用更专门的数据结构。还有个细节是unique返回的是数组如果需要保持原来的数据类型比如pandas的Series要注意后续处理。有时候人们会忘记这一点导致一些类型相关的错误。和同类技术对比除了uniquePython里还有其他去重的方法。最直接的是用内置的setmy_list[1,2,2,3,3,3]unique_setset(my_list)set的去重速度通常很快但它不保持元素的顺序也不保证返回的类型。unique会保持首次出现的顺序在某些实现中并且返回的是数组。在pandas生态里还有个drop_duplicates方法它和unique有点像但用途不同。drop_duplicates是DataFrame或Series的方法用于删除重复的行而unique是提取不重复的值。如果只是要唯一值列表用unique如果要删除数据中的重复行用drop_duplicates。另一个相关的概念是唯一性约束这在数据库设计和数据验证中很重要。unique方法可以用来检查数据是否满足唯一性约束但它本身并不强制执行约束。从实现角度看unique的算法通常基于哈希表这也是它效率较高的原因。不过具体实现可能因库的版本而异了解底层原理有助于在特定场景下做出更好的选择。实际工作中选择哪种去重方# # 关于Python里的copy你可能需要知道这些在Python里处理数据的时候经常会遇到一个让人困惑的情况明明只是想把一个列表赋值给另一个变量修改新变量时却发现原来的列表也跟着变了。这种情况第一次遇到时确实会让人愣一下。这到底是什么情况要理解这个问题得从Python处理数据的方式说起。在Python里变量更像是标签而不是盒子。当写下a [1, 2, 3]时Python在内存中创建了一个列表对象然后给这个对象贴上了a这个标签。如果接着写b a这并没有创建一个新的列表只是给同一个列表对象又贴了一个b标签。所以通过b修改列表通过a看到的自然也是修改后的结果。这就是为什么需要copy——当需要两个独立的数据副本时一个变了不影响另一个。copy能解决什么问题想象一下在整理照片。如果只是给同一张照片创建快捷方式那么无论通过哪个快捷方式编辑照片原始照片都会改变。而真正的复制则是创建一张完全相同的照片文件编辑副本时原始照片保持不变。Python的copy模块就提供了这种“真正的复制”能力。它主要解决的就是数据独立性问题特别是在处理嵌套结构时——比如列表里套列表字典里套字典这种情况。两种不同的复制方式Python提供了两种复制方式理解它们的区别很重要。浅复制shallow copy只复制最外层容器里面的元素还是原来那些。用list()构造函数或者切片操作[:]创建的就是浅复制。copy模块里的copy()函数也是做浅复制。original[[1,2],[3,4]]shallow_copyoriginal.copy()shallow_copy[0][0]99# 这时original[0][0]也变成了99深复制deep copy则是彻底复制连嵌套的内容也一并复制。用copy模块的deepcopy()函数实现。importcopy original[[1,2],[3,4]]deep_copycopy.deepcopy(original)deep_copy[0][0]99# original保持不变选择哪种方式取决于实际需求。如果确定嵌套结构里的元素不需要独立或者那些元素本身就是不可变的比如数字、字符串浅复制就足够了而且更快更省内存。什么时候该用copy实际工作中有几类情况特别需要注意。处理配置数据时经常需要copy。比如有一个默认配置字典要根据不同场景调整参数。如果直接赋值修改一个配置会影响所有场景。这时候就需要深复制一份配置副本。在函数里修改传入的可变参数时也要小心。如果函数内部修改了传入的列表或字典函数外部的原始数据也会改变。这有时候是故意的原地修改有时候则是bug。如果不希望影响原始数据就应该先复制一份。还有在缓存计算结果时。有时候会把中间结果存起来复用但如果这些结果包含可变对象直接存储引用可能会导致意料之外的数据污染。一些实际经验使用copy时有些细节值得注意。对于自定义对象copy的行为取决于这个对象如何定义__copy__()和__deepcopy__()方法。如果没定义Python会尝试用默认逻辑处理但复杂对象可能需要自定义复制逻辑。性能方面深复制比浅复制慢得多特别是嵌套很深的大数据结构时。如果不需要完全独立可以考虑混合策略——只对需要独立的部分做深复制。还有个常见误区是认为所有对象都需要深复制。实际上如果数据结构里包含文件句柄、数据库连接这类资源型对象深复制它们通常没有意义甚至可能导致问题。和其他语言的对比如果熟悉其他编程语言可能会发现Python的copy机制有些特别。像C这类语言有显式的拷贝构造函数复制行为完全由程序员控制。Java则区分基本类型和对象类型基本类型直接复制值对象类型复制引用。Python的统一对象模型让事情简单了些但也增加了理解成本。JavaScript的情况和Python有点像也是引用传递也需要显式复制。不过JavaScript没有内置的深复制函数得自己实现或者用第三方库。Python的copy模块设计得相对直观特别是区分浅复制和深复制这在处理复杂数据时很有帮助。不过也因为这种灵活性需要更清楚地知道自己在做什么。最后一点想法理解copy不仅仅是记住几个函数怎么用更是理解Python对象模型的重要一步。每次遇到数据意外被修改的情况都是一个重新思考数据流向的机会。实际编码时与其总是纠结该用哪种copy不如在设计数据结构时多考虑一下——是否可以用不可变对象是否可以减少嵌套深度好的数据结构设计往往能减少对copy的依赖。这些经验都是在实际项目中慢慢积累的特别是调试那些因为共享引用导致的诡异bug时印象会特别深刻。Python的这种方式初看可能有点绕但习惯了之后会发现它提供了一种很灵活的数据处理方式。法取决于具体需求。如果只是简单地去重且不关心顺序set就够用了。如果需要保持顺序或者进行更复杂的数据分析unique通常是更好的选择。在pandas的数据处理流程中unique往往能更自然地融入整个数据处理链条。去重看起来是个小操作但在数据质量管理和分析中却很重要。好的数据清洗习惯往往体现在对这些细节的把握上。unique这样的工具用好了能让数据分析工作更加顺畅。

更多文章