开发者

python: convert "5,4,2,4,1,0" into [[5, 4], [2, 4], [1, 0]]

Is there a "straightforward" way to convert a str containing numbers into a list of [x,y] ints?

# 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]]

By the way, the following works, but wouldn't call it straightforward... Also, it can be assumed that the input str has been validated to make sure that it only contains an even number of numbers interleaved by commas.

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 i开发者_JS百科s 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)


There are two important one line idioms in Python that help make this "straightforward".

The first idiom, use zip(). From the Python documents:

The left-to-right evaluation order of the iterables is guaranteed. This makes possible an idiom for clustering a data series into n-length groups using zip(*[iter(s)]*n).

So applying to your example:

>>> 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')]

That produces tuples each of length 2.

If you want the length of the sub elements to be different:

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

The second idiom is list comprehensions. If you want sub elements to be lists, wrap in a comprehension:

>>> [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']]

Any sub element groups that are not complete will be truncated by zip(). So if your string is not a multiple of 2, for example, you will loose the last element.

If you want to return sub elements that are not complete (ie, if your num_str is not a multiple of the sub element's length) use a slice idiom:

>>> 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']]

If you want each element to be an int, you can apply that prior to the other transforms discussed here:

>>> nums=[int(x) for x in num_str.split(",")]
>>> zip(*[iter(nums)]*2)
# etc etc etc

As pointed out in the comments, with Python 2.4+, you can also replace the list comprehension with a Generator Expression by replacing the [ ] with ( ) as in:

 >>> 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...

If your string is long, and you know that you only need 2 elements, this is more efficient.


One option:

>>> 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')]

Reference: str.split(), zip(), General information about sequence types and slicing

If you actually want integers, you could convert the list to integers first using map:

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

Explanation:

split creates a list of the single elements. The trick is the slicing: the syntax is list[start:end:step]. l[::2] will return every second element starting from the first one (so the first, third,...), whereas the second slice l[1::2] returns every second element from the second one (so the second, forth, ...).

Update: If you really want lists, you could use map again on the result list:

>>> xy_list = map(list, xy_list)

Note that @Johnsyweb's answer is probably faster as it seems to not do any unnecessary iterations. But the actual difference depends of course on the size of the list.


#!/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)]

Taken from @martineau's answer to my question, which I have found to be very fast.

Output:

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


First, use split to make a list of numbers (as in all of the other answers).

num_list = num_str.split(",")

Then, convert to integers:

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

Then, use the itertools groupby recipe:

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)

Of course, you can compress this into a single line if you're frugal:

pair_list = grouper(2, [int(i) for i in num_str.split(",")]


>>> 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]]
>>>


EDIT: @drewk cleaned this up to handle even or odd length lists:

>>> 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]]


You can shorten the first part (converting "1,2,3" to [1, 2, 3]) by using the split function:

num_list = num_str.split(",")

There might be an easier way to get pairs, but I'd do something like this:

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])

Also, since these are all lists of a defined length (2), you should probably use a tuple:

xy_pairs.append((x, y))


It may be interesting to have a generator. Here's a generator expression:

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) )


#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 :)
"""


This is a more generalized function which works for different chunk sizes and appends the reminder if needed

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)

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


Maybe this?

a = "0,1,2,3,4,5,6,7,8,9".split(",")
[[int(a.pop(0)), int(a.pop(0))] for x in range(len(a)/2)]
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜