当key是版本时按key排序

问题描述 投票:0回答:4

我正在尝试打印一本字典(实际上是一个

defaultdict
),其中键是版本号(形式为
6.0
6.1.2
),值是浮点数。

# Parse file contents
firmware_percents = defaultdict(float)
with open('test.csv', 'r') as file:
    reader = csv.DictReader(file)

    for row in reader:
        # Don't care about iphone vs ipad
        # Must be a string - StrictVersion is apparently unhashable
        rev = row["Firmware Version"].split(" ")[2]

        # Dump extra spaces, the less than (assume .1%), and % sign
        percent = float(row["% of Sessions"].strip().lstrip("<").strip("%"))
        firmware_percents[rev] += percent

def pretty_print(d):
    for k in sorted(d, key=d.get, reverse=True):
        print("{0}: {1:.1f}".format(k, d[k]))

print("All versions:")
pretty_print(firmware_percents)

但是,当我这样做时,某些版本的打印顺序不正确:

All versions:
7.0: 44.2
7.0.2: 25.7
6.1.3: 14.2
6.1.4: 5.5
7.0.1: 3.2
6.1: 2.7
# You get the point

使用此输入文件:

Firmware Version,Sessions,% of Sessions
" iPhone 5.0.1 ",20," <0.1% "
" iPhone 6.0 ",26," 0.1% "
" iPhone 5.1.1 ",69," 0.3% "
" iPhone 5.1 ",2," <0.1% "
" iPhone 7.0 ",7401," 31.5% "
" iPhone 6.1 ",337," 1.4% "
" iPhone 6.1.3 ",2193," 9.3% "
" iPhone 6.1.2 ",84," 0.4% "
" iPhone 7.0.1 ",747," 3.2% "
" iPhone 7.0.2 ",4619," 19.7% "
" iPhone 6.0.1 ",37," 0.2% "
" iPhone 6.0.2 ",1," <0.1% "
" iPhone 6.1.4 ",1281," 5.5% "
" iPad 5.0 ",4," <0.1% "
" iPad 5.1 ",100," 0.4% "
" iPad 5.1.1 ",545," 2.3% "
" iPad 6.0 ",16," <0.1% "
" iPad 6.1 ",305," 1.3% "
" iPhone 7.0.3 ",1," <0.1% "
" iPhone 6.1.1 ",1," <0.1% "
" iPad 7.0 ",2979," 12.7% "
" iPad 6.0.1 ",100," 0.4% "
" iPad 6.1.3 ",1139," 4.9% "
" iPad 6.0.2 ",5," <0.1% "
" iPad 6.1.2 ",65," 0.3% "
" iPad 7.0.2 ",1404," 6.0% "

我已经尝试过

pprint
,虽然它确实按正确的顺序排序,但它没有格式化浮点数(所以我最终得到像 14.5999999996 这样的数字)。当我尝试只做主要版本时,它偶尔也会打印出奇怪的东西,比如
defaultdict(<class 'float'>, {'5': 3.3, '6': 24.2, '7': 73.2})

如何确保这些按版本顺序打印并带有格式化的百分比?

按顺序我的意思是它按主要排序,然后按次要排序,然后按构建/超级次要排序(7.0.2 > 7.0.1 > 6.1.4 > 6.1 等等)?

python sorting dictionary python-3.x
4个回答
6
投票

我认为@dornhege 首先发现了主要问题:

key=d.get
意味着您是按值排序,而不是按键排序。

我会利用 stdlib 包

distutils
来自动处理版本内容:

>>> sorted(firmware_percents, key=distutils.version.StrictVersion, reverse=True)
['7.0.3', '7.0.2', '7.0.1', '7.0', '6.1.4', '6.1.3', '6.1.2', '6.1.1', '6.1', '6.0.2', '6.0.1', '6.0', '5.1.1', '5.1', '5.0.1', '5.0']
>>> sorted(firmware_percents, key=distutils.version.LooseVersion, reverse=True)
['7.0.3', '7.0.2', '7.0.1', '7.0', '6.1.4', '6.1.3', '6.1.2', '6.1.1', '6.1', '6.0.2', '6.0.1', '6.0', '5.1.1', '5.1', '5.0.1', '5.0']

这比按

.
拆分有优势,因为它可以更智能地处理
7.0.2rc1
之类的事情。


3
投票

将版本号转换为整数列表以进行排序:

def pretty_print(d):
    for k in sorted(d, key=lambda x: x.split("."), reverse=True):
        print("{0}: {1:.1f}".format(k, d[k]))

不过,使用

operator.methodcaller
可能会更有效一些。

from operator import methodcaller
def pretty_print(d):
    for k in sorted(d, key=methodcaller('split', '.'), reverse=True):
        print("{0}: {1:.1f}".format(k, d[k]))

0
投票

在Python中,字典是不可排序的 - 并且您的配方使用字典的“get”方法作为排序键,是按字典值排序 - 而不是按键排序。

所以,当你打电话时

for k in sorted(d, key=d.get, reverse=True)
发生的情况是,字典中的每个键都被传递给字典的“get”方法 - 检索值。

如果您想按键排序,则根本不需要将任何值传递给“key”。做就是了:

for k in sorted(d,  reverse=True):

但是,像“10.1”这样的版本将比“2.0”先出现,因为它是字符串比较 - 在“.”处分割版本号并将每个部分转换为数字将产生正确的比较:

for k in sorted(d, key=lambda x: tuple(int(n) for n in x.split(".")) , reverse=True):


0
投票

您想要的是对字符串列表进行排序,但不按字典顺序排序。相反,您希望它被排序,就好像每个字符串被分成整数元组,可能还包括连字符和空格等其他东西。

你可以做类似的事情

#!/bin/python3

import re
import json

d = {
    "7.0":            "44.2",
    "7.0.20":         "25.7",
    "7.0.20-bbb":  "25.7",
    "7.0.20-aaa":  "25.7",
    "7.0.2":          "25.7",
    "7.0.3":          "25.7",
    "6.1.3":          "14.2",
    "6.1.4":          "5.5",
    "7.0.1":          "3.2",
    "6.1":            "2.7"
}

def sort(kv):
    return tuple((
        int(v) if v.isdigit() else v
        for v in re.split('[.-]', kv[0])
        ))

d = dict(sorted(d.items(), key=sort))

print(json.dumps(d, indent=2))
© www.soinside.com 2019 - 2024. All rights reserved.