通过值对包含unicode的字典列表进行排序
我正在练习使用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
就变成了-1
,1045
就变成了-1045
。现在,如果我们按升序排序,-1045
是所有值中最小的,所以它会排在最前面,接下来是200
,最后是1
。
补充:正如Padraic在评论中提到的,如果Open
会有小数,你可以用float
函数,而不是int
。