这个其实就是 Python 的 sorted 方法的使用。
你只要在这句代码后面加上类似这样的日志,即可理解:
count_pairs = sorted(counter.items(), key=lambda x: (-x[1], x[0]))
print("######### count_pairs BEGIN #########")
print(count_pairs)
print("######### count_pairs END #########")
然后你运行 python train.py, 它会输出:
######### count_pairs BEGIN #########
[
('the', 50770), ('<unk>', 45020), ('<eos>', 42068),
('N', 32481), ('of', 24400), ('to', 23638), ('a', 21196),
('in', 18000), ('and', 17474) ... ## 此处省略很多对
('sim', 1), ('snack-food', 1), ('ssangyong', 1), ('swapo', 1),
('wachter', 1)
]
######### count_pairs END #########]
你可以看到,
the 这个单词在文本里出现频次最多,有 50770 次
<unk> (表示 unknown(未知))出现第二多,有 45020 次
sim 这个单词的出现频次是最少之一,有 1 次
等等。
可以看到这些对(pair)都是按照出现频次降序排列。
因为我们的代码经过 Counter 处理过之后,就从之前的只有单词,变成了 (单词,出现频次数目) 这样一对对的:
# 用 Counter 统计单词出现的次数,为了之后按单词出现次数的多少来排序
counter = collections.Counter(data)
然后 counter 又被传递给 sorted 方法去进行排序,那么根据什么来进行排序呢?就是这里的 key(关键字)这个参数发挥作用的时候了:
key=lambda x: (-x[1], x[0]))
lambda 是一个匿名函数。这里的 x 表示我们给 sorted 方法的输入 counter。优先按照 -x[1] 来排序,如果 -x[1] 相同的情况下,再按照 x[0] 来排序。
你可以看到对于每一个对(pair),例如 ('and', 17474),如果说它是 x,那么它的第一个元素是 x[0],也就是 'and' 这个单词;它的第二个元素是 17474,表示它的出现频次,是 17474 次。
因为如果不加 -x[1] 那个负号,那么就是按照正序排列(从小到大的顺序):1, 2, 3, 4, 5, 6, ... 17474, 18000, 21196, 23638, 24400, 32481, 42068, 45020, 50770
但是加了负号,就变成按照逆序排列了:-50770, -45020, -42068, -32481, -24400, -23638, -21196, -18000, -17474, ... -6, -5, -4, -3, -2, -1
这样就实现了我注释里说的:
# 构造从单词到唯一整数值的映射
# 后面的其他数的整数值按照它们在数据集里出现的次数多少来排序,出现较多的排前面
# 单词 the 出现频次最多,对应整数值是 0
# <unk> 表示 unknown(未知),第二多,整数值为 1
然后,因为 x[0] 表示如果在 -x[1] 相同(也就是出现频次数目相同)时,按照单词本身的字典顺序来排列,比如日志里输出的:
('good', 372), ('used', 372)
这两个单词,good 和 used,它们的输出频次都是 372 次,但 good 排在 used 前面,因为在字母顺序里,g 在 u 前面。