Python:将“5,4,2,4,1,0”转换为[[5, 4],[2, 4],[1, 0]]。

8
有没有一种“简单明了”的方法将包含数字的字符串转换为[x,y]整数列表?
# from: '5,4,2,4,1,0,3,0,5,1,3,3,14,32,3,5'
# to: [[5, 4], [2, 4], [1, 0], [3, 0], [5, 1], [3, 3], [14, 32], [3, 5]]

顺便提一句,下面的方法是可行的,但并不简单... 同时,假设输入的字符串已经通过验证,以确保它只包含逗号交错的偶数个数字。
num_str = '5,4,2,4,1,0,3,0,5,1,3,3,14,32,3,5'
numpairs_lst = []      # ends up as [[5, 4], [2, 4], [1, 0], ...]

current_num_str = ''   # the current num within the str; stop when a comma is found
xy_pair = []           # this is one of the [x,y] pairs -> [5, 4] 
for ix,c in enumerate(num_str):
    if c == ',':
        xy_pair.append(int(current_num_str))
        current_num_str = ''
        if len(xy_pair) == 2:
            numpairs_lst.append(xy_pair)
            xy_pair = []
    else:
        current_num_str += c

# and, take care of last number...
xy_pair.append(int(current_num_str))
numpairs_lst.append(xy_pair)

重复的:https://dev59.com/62855IYBdhLWcg3wKw5Y - tokland
1
[[x,y] for x,y in zip(num_str.split(',')[::2],num_str.split(',')[1::2])] - dawg
11个回答

22

Python中有两个重要的一行成语,可以帮助使这个过程“直截了当”。

第一个成语,使用zip()。来自Python文档:

可保证对可迭代对象进行从左到右的评估顺序。这使得可以使用zip(*[iter(s)]*n)将数据系列聚类为长度为n的组。

因此,应用于您的示例:

>>> num_str = '5,4,2,4,1,0,3,0,5,1,3,3,14,32,3,5'
>>> zip(*[iter(num_str.split(","))]*2)
[('5', '4'), ('2', '4'), ('1', '0'), ('3', '0'), ('5', '1'), 
('3', '3'), ('14', '32'), ('3', '5')]

产生长度为2的元组。
如果您希望子元素的长度不同:
>>> zip(*[iter(num_str.split(","))]*4)
[('5', '4', '2', '4'), ('1', '0', '3', '0'), ('5', '1', '3', '3'), 
('14', '32', '3', '5')]

第二个习惯用语是列表推导式。如果您希望子元素成为列表,请在推导式中进行包装:
>>> [list(t) for t in zip(*[iter(num_str.split(","))]*4)]
[['5', '4', '2', '4'], ['1', '0', '3', '0'], ['5', '1', '3', '3'], 
['14', '32', '3', '5']]
>>> [list(t) for t in zip(*[iter(num_str.split(","))]*2)]
[['5', '4'], ['2', '4'], ['1', '0'], ['3', '0'], ['5', '1'], ['3', '3'], 
['14', '32'], ['3', '5']]

任何不完整的子元素组将被zip()截断。因此,如果您的字符串不是2的倍数,例如,您将失去最后一个元素。
如果您想返回不完整的子元素(即,如果您的num_str不是子元素长度的倍数),请使用slice惯用语
>>> l=num_str.split(',')
>>> [l[i:i+2] for i in range(0,len(l),2)]
[['5', '4'], ['2', '4'], ['1', '0'], ['3', '0'], ['5', '1'], 
['3', '3'], ['14', '32'], ['3', '5']]
>>> [l[i:i+7] for i in range(0,len(l),7)]
[['5', '4', '2', '4', '1', '0', '3'], ['0', '5', '1', '3', '3', '14', '32'], 
['3', '5']]

如果您想让每个元素都是整数,您可以在此之前应用其他讨论过的转换:
>>> nums=[int(x) for x in num_str.split(",")]
>>> zip(*[iter(nums)]*2)
# etc etc etc

正如评论中所指出的那样,在 Python 2.4+ 中,您还可以通过将[ ]替换为( )来使用生成器表达式替换列表推导式,例如:

 >>> nums=(int(x) for x in num_str.split(","))
 >>> zip(nums,nums)
 [(5, 4), (2, 4), (1, 0), (3, 0), (5, 1), (3, 3), (14, 32), (3, 5)]
 # or map(list,zip(nums,nums)) for the list of lists version...

如果您的字符串很长,并且您知道只需要2个元素,那么这种方法更有效。

我认为你的第一个解决方案最符合Pythonic风格,但你遗漏了OP所请求的使用map(int)将字符串转换为整数。 - PaulMcG
要获取数字元组而不是字符串,您可以使用zip(*[imap(int, num_str.split(","))]*2)(使用itertools.imap())。 - Sven Marnach
1
在这种情况下,生成器的问题在于生成器没有长度并且不可子脚本化;因此,您无法使用支持部分子列表的“切片惯用语”。鉴于字符串已经在内存中,并且生成的列表也将在内存中,我认为生成器更多是理论上的而不是实际上的。+1,你的Python正在进步! - the wolf
2
@Johnsyweb:我认为列表推导和生成器表达式是Python中真正美丽的特性之一——我喜欢它们!Perl有类似的结构;如果你理解了它们,它们更加灵活;但比Python等价物*不太可读。对我来说(这是Zen和Perl背景的主观解释),难以理解的是Python对小型辅助函数的偏好。与将所有内容放在一行中相比,必须跟踪这些小函数。我猜这是一种权衡。谢谢你的评论。由于有益的评论,我每天都学到更多。:-} - dawg
2
@drewk:我完全同意。小助手函数的好处在于它们非常容易进行单元测试,并且(通过有意义的名称)使得阅读“你正在做什么”(而不是“你如何做到这一点”)变得容易。因此,如果您在互联网上搜索“可执行伪代码”,您会发现很多关于Python的结果! - johnsyweb
显示剩余8条评论

15
一个选项:
>>> num_str = '5,4,2,4,1,0,3,0,5,1,3,3,4,3,3,5'
>>> l = num_str.split(',')
>>> zip(l[::2], l[1::2])
[('5', '4'), ('2', '4'), ('1', '0'), ('3', '0'), ('5', '1'), ('3', '3'), ('4', '3'), ('3', '5')]

参考: str.split(), zip(), 关于序列类型和切片的一般信息

如果您确实需要整数,您可以首先使用map将列表转换为整数:

>>> l = map(int, num_str.split(','))

解释:

split 函数会创建一个由单个元素组成的列表。关键是分片:语法为 list[start:end:step]l[::2] 会从第一个元素开始返回每隔一个元素的元素(因此是第一个、第三个等),而第二个分片 l[1::2] 会从第二个元素开始返回每隔一个元素的元素(因此是第二个、第四个等)。

更新: 如果您真的想要列表,可以再次在结果列表上使用 map 函数:

>>> xy_list = map(list, xy_list)

请注意,@Johnsyweb的答案可能更快,因为它似乎没有进行任何不必要的迭代。但实际差异当然取决于列表的大小。

@A A:您想了解什么? - Felix Kling
嗨,Felix,这个有效 - 谢谢。这只是小问题,我们能否使zip()返回2项列表而不是元组? - jd.
在切片列表时,您可以指定(最多)三个由冒号分隔的参数。第一个是拆分的开始位置(默认为0),第二个是拆分的结束位置(默认为列表的末尾),第三个是步长(默认为1)。因此,::2将从0到末尾拆分列表,并获取每个其他元素,而1::2将从1到末尾拆分列表并获取每个其他元素。 - Wilduck
@jd:为什么你需要列表?通常,唯一的区别是列表是可变的 - 你需要这个吗? - user395760
@delnan:不是必须的,但我想保留某人在管道中添加额外元素到2元列表的可能性。 - jd.
显示剩余2条评论

11
#!/usr/bin/env python

from itertools import izip

def pairwise(iterable):
    "s -> (s0,s1), (s2,s3), (s4, s5), ..."
    a = iter(iterable)
    return izip(a, a)

s = '5,4,2,4,1,0,3,0,5,1,3,3,4,3,3,5'
fields = s.split(',')
print [[int(x), int(y)] for x,y in pairwise(fields)]

摘自@martineau的答案我的问题,我发现这非常快。

输出:

[[5, 4], [2, 4], [1, 0], [3, 0], [5, 1], [3, 3], [4, 3], [3, 5]]

3

首先,使用split将数字列表化(与其他答案中的做法一致)。

num_list = num_str.split(",")

然后,将其转换为整数:

num_list = [int(i) for i in num_list]

接下来,使用 itertools groupby 函数:

from itertools import izip_longest
def grouper(n, iterable, fillvalue=None):
   "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
   args = [iter(iterable)] * n
   return izip_longest(fillvalue=fillvalue, *args)

pair_list = grouper(2, num_list)

当然,如果你很节约的话,你可以将这个压缩成一行:
pair_list = grouper(2, [int(i) for i in num_str.split(",")]

2
>>> num_str = '5,4,2,4,1,0,3,0,5,1,3,3,4,3,3,5'
>>> inums = iter([int(x) for x in num_str.split(',')])
>>> [[x, inums.next()] for x in inums]
[[5, 4], [2, 4], [1, 0], [3, 0], [5, 1], [3, 3], [4, 3], [3, 5]]
>>>

  1. 在第二行中,您可以省略iter和方括号,它仍然可以工作。
  2. next(inums)inums.next()更可取,因为这将使解决方案在Python 3.x中也能正常工作。
  3. 如果您愿意使用元组而不是列表,则最后一行可以写成zip(inums,inums)
- Sven Marnach
@Sven Marnach: (1) & (2): 你说得对,对于最新的Python版本来说,我的代码通常要支持2.1到2.7之间的包。 :-) (3) 我个人对元组没问题,但是楼主想要列表。 - John Machin
我也体验到,在实践中支持 Python 2.1 似乎比支持 3.x 更重要。 - Sven Marnach

1

编辑:@drewk 对处理偶数或奇数长度的列表进行了优化:

>>> f = '5,4,2,4,1,0,3,0,5,1,3,3,14,32,3,5'
>>> li = [int(n) for n in f.split(',')]
>>> [li[i:i+2] for i in range(0, len(li), 2)]
[[5, 4], [2, 4], [1, 0], [3, 0], [5, 1], [3, 3], [14, 32], [3, 5], [7]]

0

您可以使用split函数来缩短第一部分(将“1,2,3”转换为[1,2,3]):

num_list = num_str.split(",")

可能有更简单的方法来获取成对数据,但我会像这样做:

xy_pairs = []
for i in range(0, len(num_list), 2):
    x = num_list[i]
    y = num_list[i + 1]
    xy_pairs.append([x, y])

另外,由于这些都是已定义长度(2)的列表,您应该使用元组:

xy_pairs.append((x, y))

0

拥有一个生成器可能会很有趣。这里是一个生成器表达式:

import re
ch = '5,4,2,4,1,0,3,0,5,1,3,3,14,32,3,5'
genexp = ( map(int,ma.groups()) for ma in re.finditer('(\d+)\s*,\s*(\d+)',ch) )

0
#declare the string of numbers
str_nums = '5,4,2,4,1,0,3,0,5,1,3,3,14,32,3,5'

#zip two lists: the even elements with the odd elements, casting the strings to integers
zip([int(str_nums.split(',')[i]) for i in range(0,len(str_nums.split(',')),2)],[int(str_nums.split(',')[i]) for i in range(1,len(str_nums.split(',')),2)])

"""
Of course you would want to clean this up with some intermediate variables, but one liners like this is why I love Python :)
"""

0

这是一个更通用的函数,适用于不同的块大小,并在需要时附加剩余部分

def breakup(mylist,chunks):
  mod = len(mylist) % chunks
  if mod ==  0:
      ae = []
  elif mod == 1:
      ae = mylist[-1:]
  else:
      ae = [tuple(mylist[-mod:])]
  return zip(*[iter(mylist)]*chunks) + ae

num_str = '5,4,2,4,1,0,3,0,5,1,3,3,14,32,3,5'
lst = map(int,num_str.split(','))
print breakup(lst,2)

输出:[(5, 4), (2, 4), (1, 0), (3, 0), (5, 1), (3, 3), (14, 32), (3, 5)]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接