资讯专栏INFORMATION COLUMN

Python3 CookBook | 数据结构和算法(二)

geekidentity / 1259人阅读

摘要:以下测试代码全部基于查找最大或最小的个元素工作中有时会遇到这样的需求,取出数据中前面的值,或者最后的值。大家如果对堆数据结构感兴趣的话,可以继续进行深入研究,由于我了解的并不深,也没办法再展开了。

文章首发于知乎专栏,欢迎关注。
https://zhuanlan.zhihu.com/py...

以下测试代码全部基于 Python3

1、查找最大或最小的 N 个元素

工作中有时会遇到这样的需求,取出数据中前面 10% 的值,或者最后 10% 的值。

我们可以先对这个列表进行排序,然后再进行切片操作,很轻松的解决这个问题。但是,有没有更好的方法呢?

heapq 模块有两个函数 nlargest() 和 nsmallest() 可以完美解决这个问题。

In [50]: import heapq

In [51]: n = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2, 23, 45, 76]

In [52]: heapq.nlargest(3, n)
Out[52]: [76, 45, 42]

In [53]: heapq.nsmallest(3, n)
Out[53]: [-4, 1, 2]

如果是取排在前面的 10% 应该怎么做?

heapq.nlargest(round(len(n)/10), n)

而且,使用这两个函数还会有更好的性能,因为在底层实现里面,会先把数据进行堆排序后放入一个列表中,然后再进行后续操作。大家如果对堆数据结构感兴趣的话,可以继续进行深入研究,由于我了解的并不深,也没办法再展开了。

但是也并不是什么时候都是这两个函数效果更好,比如只取一个最大值或者最小值,那还是 min() 或 max() 效果更好;如果要查找的元素个数已经跟集合元素个数接近时,那还是用 sorted(items)[:N] 更好,具体情况具体分析吧。

2、序列中出现次数最多的元素

以前碰到这类问题时,我都会手动创建一个字典,然后以列表中元素作为 key,进而统计出 key 出现的次数,再进行比较得到出现次数最多的元素。

殊不知 collections 中就有专门为这类问题设计的类 Counter,瞬间感觉自己蠢爆了,话不多说,直接上代码。

In [54]: from collections import Counter

In [55]: w = ["a", "b", "c", "d", "a", "a", "b"]

In [56]: w_count = Counter(w)

In [57]: w_count
Out[57]: Counter({"a": 3, "b": 2, "c": 1, "d": 1})

In [58]: w_count["a"]
Out[58]: 3

In [59]: top = w_count.most_common(2)

In [60]: top
Out[60]: [("a", 3), ("b", 2)]

可以看到,Counter 返回的就是一个字典,想知道哪个元素出现几次,直接取,是不是很方便?

而且还有 most_common 函数,简直不要太棒。

3、过滤序列元素

有一个列表,如下:

In [61]: a = [1, 2, 3, 4, 5, -3]

要求过滤所有负数。需要新建一个列表?直接一行代码搞定。

In [64]: [n for n in a if n > 0]
Out[64]: [1, 2, 3, 4, 5]

如果要把负数替换成 0 呢?

In [67]: [n if n > 0 else 0 for n in a]
Out[67]: [1, 2, 3, 4, 5, 0]

但是有时候过滤条件可能比较复杂,这时就需要借助于 filter() 函数了。

values = ["1", "2", "-3", "-", "4", "N/A", "5"]
def is_int(val):
  try:
    x = int(val)
      return True
  except ValueError:
    return False

ivals = list(filter(is_int, values))
print(ivals)
# Outputs ["1", "2", "-3", "4", "5"]
4、通过某个关键字将记录分组

有下面这个字典:

rows = [
  {"address": "5412 N CLARK", "date": "07/01/2012"},
  {"address": "5148 N CLARK", "date": "07/04/2012"},
  {"address": "5800 E 58TH", "date": "07/02/2012"},
  {"address": "2122 N CLARK", "date": "07/03/2012"},
  {"address": "5645 N RAVENSWOOD", "date": "07/02/2012"},
  {"address": "1060 W ADDISON", "date": "07/02/2012"},
  {"address": "4801 N BROADWAY", "date": "07/01/2012"},
  {"address": "1039 W GRANVILLE", "date": "07/04/2012"},
]

那么怎么对这个字典按照 date 进行分组呢?借助于 itertools.groupby() 函数可以解决这个问题,代码如下:

# Sort by the desired field first
rows.sort(key=itemgetter("date"))
# Iterate in groups
for date, items in groupby(rows, key=itemgetter("date")):
  print(date)
  for i in items:
    print(" ", i)

输出结果如下:

07/01/2012
  {"address": "5412 N CLARK", "date": "07/01/2012"}
  {"address": "4801 N BROADWAY", "date": "07/01/2012"}
07/02/2012
  {"address": "5800 E 58TH", "date": "07/02/2012"}
  {"address": "5645 N RAVENSWOOD", "date": "07/02/2012"}
  {"address": "1060 W ADDISON", "date": "07/02/2012"}
07/03/2012
  {"address": "2122 N CLARK", "date": "07/03/2012"}
07/04/2012
  {"address": "5148 N CLARK", "date": "07/04/2012"}
  {"address": "1039 W GRANVILLE", "date": "07/04/2012"}

需要注意的是,groupby() 函数仅仅检查连续相同的元素,所以在分组之前,一定要先对数据,按照分组字段进行排序。如果没有排序,便得不到想要的结果。

5、映射名称到序列元素

我常常有这样的苦恼,就是有一个列表,然后通过下标来取值,取值时很认真的数所需要元素在第几个,很怕取错值。取到值后开始下面的运算。

一段时间之后,再看这段代码,感觉很陌生,已经忘了带下标的值是什么了,还需要重新看一下这个列表的由来,才找到回忆。

如果能有一个名称映射到元素上就好了,直接通过名称就可以知道元素的含义。collections.namedtuple() 函数就可以解决这个问题。

In [76]: from collections import namedtuple

In [77]: subscriber = namedtuple("Subscriber", ["addr", "joined"])

In [78]: sub = subscriber("jonesy@example.com", "2012-10-19")

In [79]: sub
Out[79]: Subscriber(addr="jonesy@example.com", joined="2012-10-19")

In [80]: sub.addr
Out[80]: "jonesy@example.com"

In [81]: sub.joined
Out[81]: "2012-10-19"

这样就可以通过名称来取值了,代码可读性也更高。

需要注意的是,这种命名元祖的方式不能直接修改其中的值,直接修改会报错

In [82]: a = namedtuple("SSS", ["name", "shares", "price"])

In [83]: _a = a("yongxinz", 1, 2)

In [84]: _a.shares = 4
-----------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
 in ()
> 1 _a.shares = 4

AttributeError: can"t set attribute

想要修改的话可以使用 _replace() 函数。

In [85]: _a._replace(shares=4)
Out[85]: SSS(name="yongxinz", shares=4, price=2)

但是还有一个疑问,如果这个列表元素比较多的话,那就需要定义很多的名称,也比较麻烦,还有更好的方式吗?

未完待续。。。

欢迎留言,或添加我个人微信 zhangyx6a 交流,不是微商。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/44511.html

相关文章

  • Python3 CookBook | 数据结构算法(一)

    摘要:提供了大量的内置数据结构,包括列表,集合以及字典。这个问题很简单,我的第一反应是循环求和,然后计算平均值,显然很麻烦。这时候就该登场了,它的作用是可以使键和值反转过来。 文章首发于知乎专栏,欢迎关注。https://zhuanlan.zhihu.com/py... 以下测试代码全部基于 Python3。 Python 提供了大量的内置数据结构,包括列表,集合以及字典。在工作和编码中,可...

    崔晓明 评论0 收藏0
  • Python: 你不知道的 super

    摘要:整个过程还是比较清晰的,关键是要理解的工作方式,而不是想当然地认为调用了父类的方法。小结事实上,和父类没有实质性的关联。 super() 的入门使用 在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我们就需要调用父类的方法了,可通过使用 super 来实现,比如: class Animal(object): def __...

    darkerXi 评论0 收藏0
  • 超实用的 Python 学习资源汇总

    摘要:链接中文翻译常用库推荐除了上面提到的之外,也是一个很好的选项。官网中文翻译如果你要编写简单的爬虫,来爬去互联网上的信息,或者调用一些外部的机遇的接口,使用这个库再也合适不过了。 作者:安龙 LeanCloud 工程师 引言 :前段时间有同学反映 Python 的学习资源汇总很少。那么学习资料哪个质量更好,效率更高?Python 有哪些非常值得学习的库?2017 年了学 2 还是学 3 ...

    宠来也 评论0 收藏0
  • 每天学点Python Cookbook

    摘要:过滤字符串中不属于指定集合的字符任务给定一个需要保留的字符的集合,构建一个过滤函数,并可将其应用于任何字符串,函数返回一个的拷贝,该拷贝只包含指定字符集合中的元素。用方法用于创建字符映射的转换表,具体代码如下测试用例测试结果 1.过滤字符串中不属于指定集合的字符 任务:给定一个需要保留的字符的集合,构建一个过滤函数,并可将其应用于任何字符串s,函数返回一个s的拷贝,该拷贝只包含指定字...

    GitChat 评论0 收藏0
  • Python Tips

    摘要:的三种数据类型字典列表元组,分别用花括号中括号小括号表示。约等于上句,可能是因为自定义变量名与内部函数或变量同名了。下,默认路径一般为。的日志模块中计时器定时器计划任务,。对象的问题怎样忽略警告不打印烦人的警告打印到终端同时记录到文件。 Python Enhancement Proposal。(PEP,Python增强建议书) Python之禅(import this) Pytho...

    Reducto 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<