個々の公共の番号でこの記事の発信元:TechFlow、オリジナリティが信者を求めて、簡単ではありません
今日は6件のPythonの話題、記事3つの非常に不思議な道の中のPythonを導入することである:マップ、削減し、フィルタ。
マップを見ると、我々は大規模なデータ系列の紹介学生のMapReduce記事、おそらくいくつかの感想かの前に見てきたという感じがない場合に減らすかどうかを知るしないでください。計算方法分散MapReduceはないものが、また、どのようにPythonでの方法になるには?その理由は、Pythonは非常に若い言語であるので、それは、開発の過程であり、非常に簡単です実際には、他の分野の多くのエッセンスを吸収するために、MapReduceはそのうちの一つです。
記事に興味のある学生のためには、MapReduceの前に内容を確認するには、以下のリンクをクリックする前に。
地図
マップに加えてマッピングし、他の英語は、マッピングを主張しました。さらに、拡張マップ貯蔵容器となるキーと値のマッピングの構造となり、いくつかの言語、C ++およびJavaで。Pythonは、血管KV名の構造は、辞書、すなわち辞書となり、その後され、元の意図に戻り、マップ、この点で区別したマップされました。
我々は、すべての数学の分野では、マッピングは関数を定義していることを知っています。特定の引数マップすることで、従属変数に対応しています。その作用の範囲は、もはや単一の変数が、配列であるが、同様に、Pythonで、操作の性質をマッピングすることは、また、機能しません。換言すれば、我々は、マップサイクル動作を介して自動的に容器の機能を適用することができる要素を切断することができます。
ここでは、座標持っているような、単純な例では、我々は原点からの距離を知りたいです。この問題は、我々は距離を解決することができる計算する関数を記述し、非常に簡単です:
def dis(point):
return math.sqrt(point[0]**2 + point[1]**2)
私は複数のポイントを持って距離を計算する必要がある場合は、マップの前に現れ、我々は唯一の問題を解決するために、サイクルを何を使用することができます。
points = [[0, 1], [2, 4], [3, 2]]
for point in points:
print(dis(point))
但是有了map之后, 我们可以省去循环的操作,整个代码简化成了一行:
map(dis, points)
但是要注意,我们调用完map之后得到的结果不是一个list而是一个迭代器。我们直接将map返回的内容print出来,可以得到这样一个结果:
>>> print(map(dis, points))
<map object at 0x107aad1d0>
这是一个类的标准输出,其实它返回的不是最后的结果,而是一个迭代器。我们在之前的文章当中已经介绍过了迭代器和生成器的相关概念,这里不多做赘述了,遗忘的同学可以点击下方链接回顾一下之前的内容:
我们想要获得完整的内容也很容易,我们只需要将它转化成list类型即可:
>>> print(list(map(dis, points)))
[1.0, 4.47213595499958, 3.605551275463989]
以上过程还可以进一步简化,还记得我们之前介绍过的匿名函数吗?由于dis函数在我们的程序当中只会在map中用到,我们完全没有必要单独创建一个函数,我们可以直接传入一个匿名函数搞定运算:
map(lambda x: math.sqrt(x[0]**2 + x[1] ** 2), points)
简单总结一下,map操作其实执行的是一个映射。它可以自动地将一个序列当中的内容通过制定的函数映射成另一个序列,从而避免显式地使用循环来调用,在很多场景下可以大大地简化代码的编写,可以很方便地将一个序列整体转变成另一个结果。
reduce
相比于map,reduce的操作稍稍难理解一点点。它也是规定一个映射,不过不是将一个元素映射成一个结果。而是将两个元素归并成一个结果。并且它并不是调用一次,而是依次调用,直到最后只剩下一个结果为止。
比如说我们有一个数组[a, b, c, d]和一个函数f,我们计算reduce(f, [a, b, c, d])其实就等价于f(f(f(a, b), c), d)。和map不同的是,reduce最后得到一个结果,而不是一个迭代器或者是list。
我们光说有些抽象,不妨来看一个例子,就看最简单的一个例子:reduce函数接收两个数,返回两个数的和。那么显然,我们依次调用reduce,得到的就是原数组的和。
from functools import reduce
def f(a, b):
return a + b
print(reduce(f, [1, 2, 3, 4]))
最终得到的结果当然是10,同样,我们也可以将reduce中的方法定义成匿名函数,一样不影响最终的结果。
print(reduce(lambda x, y: x + y, [1, 2, 3, 4]))
MapReduce
既然我们map和reduce都有了,显然我们可以将它们串联起来使用,也就是分布式系统当中MapReduce的做法。虽然如果不手动使用线程池的话,Python并不会起多个线程来加速运算,但是至少可以简化我们实现的代码。我们还是举经典的wordCount的例子,也就是文本计算词频。
套用map和reduce的功能,整个流程非常清晰,我们只需要在map阶段对文本进行分词,在reduce阶段对分词之后的结果进行汇总即可。
听着好像非常容易,但是你实际去上手是写不出来的。原因也很简单,因为hadoop当中的Map和Reduce中间还有一层shuffle的操作,会自动地将key值相同的结果放到同一个reducer当中。在这个问题当中,key自然就是我们的word,由于相同的word被放到同一个reducer当中,我们只需要累加就行了。但是如果我们自己编写mapreduce的话,由于缺少了中间数据重排的步骤,所以导致不能实现。
要解决也简单,我们可以人为增加一个map阶段代替hadoop当中的重排。相当于做了一个MapMapReduce,我们来看代码:
from collections import Counter, defaultdict
texts = ['apple bear peach grape', 'grape orange pear']
# 第一次map,将字符串转成数组,每个单词对应1
def mp1(text):
ret = []
words = text.split(' ')
for word in words:
ret.append((word, 1))
return ret
# 第二次map,将数组转成dict
def mp2(arr):
d = defaultdict(int)
for k, v in arr:
d[k] += v
return d
# reduce,合并dict
def rd(x, y):
x.update(y)
return x
print(reduce(rd, map(mp2, map(mp1, texts))))
那如果我们不用多次MapReduce呢?也不是没有办法,需要取点巧,方法也简单只要使用之前我们讲解过的Counter类,就可以完美解决这个问题。我们来看代码:
from collections import Counter
texts = ['apple bear peach grape', 'grape orange pear']
def mp(text):
words = text.split(' ')
return Counter(words)
print(reduce(lambda x, y: x + y, map(mp, texts)))
由于我们使用了Counter,所以我们在map阶段返回的结果就已经是词频的dict了,而在reduce阶段我们只需要将它们全部累加起来就OK了。
最后,我们来看下filter。
filter
filter的英文是过滤,所以它的使用就很明显了。它的用法和map有些类似,我们编写一个函数来判断元素是否合法。通过调用filter,会自动将这个函数应用到容器当中所有的元素上,最后只会保留运行结果是True的元素,而过滤掉那些是False的元素。
举个例子,假设我们想要保留list当中的奇数而过滤掉偶数,我们当然可以直接操作,比如:
arr = [1, 3, 2, 4, 5, 8]
[i for i in arr if i % 2 > 0 ]
而使用filter会非常方便:
list(filter(lambda x: x % 2 > 0, arr))
从这个例子当中可能看不出便捷,但是有的时候判断的条件可能非常复杂,我们判断的逻辑不能简单地在list定义当中表达出来,这个时候使用filter则会容易得多。
最后, 我们再看一个类似的用法。在itertools当中有一个方法叫做 compress,通过compress我们可以实现根据一个序列的条件过滤另一个序列。
举个简单的例子,假设,我们有两个数组:
student = ['xiaoming', 'xiaohong', 'xiaoli', 'emily']
scores = [60, 70, 80, 40]
我们想要获取所有考试及格的同学的list,如果用常规做法基本上免不了使用循环,但是使用compress可以很方便地通过一行代码实现:
from itemtools import compress
>>> pass = [i > 60 for i in scores]
>>> print(pass)
[False, True, True, False]
>>> list(compress(student, pass))
['xiaohong', 'xiaoli']
需要注意的是filter和compress返回的都是一个迭代器,我们要获取它们的值,需要手动转换成list。
虽然在日常的开发当中不使用这三样神器同样可以工作,但是用上它们之后,会提升很多代码的可读性,节省很多无用的代码。尤其是在面试的时候,很有可能就会给面试官留下不一样的印象,也许结果也会不同。
今天的文章就是这些,如果觉得有所收获,请顺手点个关注或者转发吧,你们的举手之劳对我来说很重要。