通过值对包含unicode的字典列表进行排序

0 投票
3 回答
1007 浏览
提问于 2025-04-18 15:45

我正在练习使用BeautifulSoup进行网页抓取,最近在查看一些期权数据。目前我把这些信息存储在一个字典的列表里。我想按照“Open”这个字段进行降序排序,但因为它是以unicode格式存储的,所以排序不太对。以下是我数据的一部分。

[{'Last': u'2.09', 'Vol': u'1', 'Open': u'200', 'Symbol': 'F', 'Strike': u'19.50', 'Ask': u'2.58', 'Date': '140816', 'Type': 'P', 'Bid': u'2.30', 'Change': u' 0.00'}, {'Last': u'1.96', 'Vol': u'1', 'Open': u'1', 'Symbol': 'F', 'Strike': u'19.50', 'Ask': u'2.60', 'Date': '140822', 'Type': 'P', 'Bid': u'2.30', 'Change': u' 0.00'}, {'Last': u'2.18', 'Vol': u'22', 'Open': u'1,045', 'Symbol': 'F', 'Strike': u'15.00', 'Ask': u'2.23', 'Date': '140816', 'Type': 'C', 'Bid': u'2.03', 'Change': u' 0.00'}]

我目前用来排序的代码如下:

from operator import itemgetter
newlist = sorted(optionQuotes, key=itemgetter('Open'))

我该怎么写代码,把它转换成整数,然后再转换回unicode呢?我尝试过的所有方法都没有成功,所以非常希望能得到一些帮助。

3 个回答

0

你可以去掉那些不是数字的部分,然后在关键函数中把它转换成整数:

def conv(s):
    try:
        return int(''.join([e for e in s if e in u'0123456789']))
    except ValueError:
        return s    

print '\n'.join(map(str, sorted(LoD, key=lambda d: conv(d['Open']), reverse=True)))

输出结果是:

{'Strike': u'15.00', 'Ask': u'2.23', 'Last': u'2.18', 'Vol': u'22', 'Type': 'C', 'Symbol': 'F', 'Open': u'1,045', 'Bid': u'2.03', 'Change': u' 0.00', 'Date': '140816'}
{'Strike': u'19.50', 'Ask': u'2.58', 'Last': u'2.09', 'Vol': u'1', 'Type': 'P', 'Symbol': 'F', 'Open': u'200', 'Bid': u'2.30', 'Change': u' 0.00', 'Date': '140816'}
{'Strike': u'19.50', 'Ask': u'2.60', 'Last': u'1.96', 'Vol': u'1', 'Type': 'P', 'Symbol': 'F', 'Open': u'1', 'Bid': u'2.30', 'Change': u' 0.00', 'Date': '140822'}

如果你想支持小数,比如在 'Last' 这个字段中,你可以使用正则表达式,这样就可以处理像 -1.23e-2 这样的浮点数:

def convf(s):
    m=re.search(r'([-+]?[0-9,]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)', s)
    if not m:
        return s
    ns=re.sub(r'[^\d\.e\-+]', '', m.group(1).lower() )   
    try:
        return float(ns)
    except ValueError:
        return s        
print '\n'.join(map(str, sorted(LoD, key=lambda d: convf(d['Last']))))

输出结果是:

{'Strike': u'19.50', 'Ask': u'2.60', 'Last': u'1.96', 'Vol': u'1', 'Type': 'P', 'Symbol': 'F', 'Open': u'1', 'Bid': u'2.30', 'Change': u' 0.00', 'Date': '140822'}
{'Strike': u'19.50', 'Ask': u'2.58', 'Last': u'2.09', 'Vol': u'1', 'Type': 'P', 'Symbol': 'F', 'Open': u'200', 'Bid': u'2.30', 'Change': u' 0.00', 'Date': '140816'}
{'Strike': u'15.00', 'Ask': u'2.23', 'Last': u'2.18', 'Vol': u'22', 'Type': 'C', 'Symbol': 'F', 'Open': u'1,045', 'Bid': u'2.03', 'Change': u' 0.00', 'Date': '140816'}
1

如果你想在原地对列表进行排序,而不想创建一个新的列表:

sorted(optionQuotes, key = lambda x : float(x.get("Open").replace(",","")), reverse = True)
optionQuotes.sort(key = lambda x: float(x.get("Open").replace(",","")), reverse=True)
3

排序默认是用字典顺序来比较的,而你现在是用字符串格式的数字来比较,这就是为什么结果和你预期的不一样。你可以把Open的值转换成int类型,这样在排序时比较就能正常工作了。

sorted(optionQuotes, key=lambda x: -int(x["Open"].replace(",", "")))

我只是把,替换成了空字符串,因为int函数无法处理包含非数字的字符串。

另外,注意在int函数结果前面有个一元运算符-。因为你想要按降序排序,所以我们只是把实际的值取反。这样,1就变成了-11045就变成了-1045。现在,如果我们按升序排序,-1045是所有值中最小的,所以它会排在最前面,接下来是200,最后是1

补充:正如Padraic在评论中提到的,如果Open会有小数,你可以用float函数,而不是int

撰写回答