Related
If I have a list c like so:
a = [1,2,3,4]
c = [a,a]
What's the simplest way of finding that it's a list of length two where each element is a list of length 4? If I do len(c) I get 2 but it doesn't give any indication that those elements are lists or their size unless I explicitly do something like
print(type(c[0]))
print(len(c[0]))
print(len(c[1]))
I could do something like
import numpy as np
np.asarray(c).shape
which gives me (2,4), but that only works when the internal lists are of equal size. If instead, the list is like
a = [1,2,3,4]
b = [1,2]
d = [a,b]
then np.asarray(d).shape just gives me (2,). In this case, I could do something like
import pandas as pd
df = pd.DataFrame(d)
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2 entries, 0 to 1
Data columns (total 4 columns):
0 2 non-null int64
1 2 non-null int64
2 1 non-null float64
3 1 non-null float64
dtypes: float64(2), int64(2)
memory usage: 144.0 bytes
From this, I can tell that there are lists inside the original list, but I would like to be able to see this without using pandas. What's the best way to look at the internal structure of a list?
Depending on the output format you expect, you could write a recursive function that returns nested tuples of length and shape.
Code
def shape(lst):
length = len(lst)
shp = tuple(shape(sub) if isinstance(sub, list) else 0 for sub in lst)
if any(x != 0 for x in shp):
return length, shp
else:
return length
Examples
lst = [[1, 2, 3, 4], [1, 2, 3, 4]]
print(shape(lst)) # (2, (4, 4))
lst = [1, [1, 2]]
print(shape(lst)) # (2, (0, 2))
lst = [1, [1, [1]]]
print(shape(lst)) # (2, (0, (2, (0, 1))))
This way is returning the type of element of list, and the first item is the parent list info.
def check(item):
res = [(type(item), len(item))]
for i in item:
res.append((type(i), (len(i) if hasattr(i, '__len__') else None)))
return res
>>> a = [1,2,3,4]
>>> c = [a,a]
>>> check(c)
[(list, 2), (list, 4), (list, 4)]
This question already has answers here:
How can I iterate over overlapping (current, next) pairs of values from a list?
(12 answers)
Closed 2 years ago.
Given a list
l = [1, 7, 3, 5]
I want to iterate over all pairs of consecutive list items (1,7), (7,3), (3,5), i.e.
for i in xrange(len(l) - 1):
x = l[i]
y = l[i + 1]
# do something
I would like to do this in a more compact way, like
for x, y in someiterator(l): ...
Is there a way to do do this using builtin Python iterators? I'm sure the itertools module should have a solution, but I just can't figure it out.
Just use zip
>>> l = [1, 7, 3, 5]
>>> for first, second in zip(l, l[1:]):
... print first, second
...
1 7
7 3
3 5
If you use Python 2 (not suggested) you might consider using the izip function in itertools for very long lists where you don't want to create a new list.
import itertools
for first, second in itertools.izip(l, l[1:]):
...
Look at pairwise at itertools recipes: http://docs.python.org/2/library/itertools.html#recipes
Quoting from there:
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return izip(a, b)
A General Version
A general version, that yields tuples of any given positive natural size, may look like that:
def nwise(iterable, n=2):
iters = tee(iterable, n)
for i, it in enumerate(iters):
next(islice(it, i, i), None)
return izip(*iters)
I would create a generic grouper generator, like this
def grouper(input_list, n = 2):
for i in xrange(len(input_list) - (n - 1)):
yield input_list[i:i+n]
Sample run 1
for first, second in grouper([1, 7, 3, 5, 6, 8], 2):
print first, second
Output
1 7
7 3
3 5
5 6
6 8
Sample run 1
for first, second, third in grouper([1, 7, 3, 5, 6, 8], 3):
print first, second, third
Output
1 7 3
7 3 5
3 5 6
5 6 8
Generalizing sberry's approach to nwise with comprehension:
def nwise(lst, k=2):
return list(zip(*[lst[i:] for i in range(k)]))
Eg
nwise(list(range(10)),3)
[(0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6), (5, 6,
7), (6, 7, 8), (7, 8, 9)]
A simple means to do this without unnecessary copying is a generator that stores the previous element.
def pairs(iterable):
"""Yield elements pairwise from iterable as (i0, i1), (i1, i2), ..."""
it = iter(iterable)
try:
prev = next(it)
except StopIteration:
return
for item in it:
yield prev, item
prev = item
Unlike index-based solutions, this works on any iterable, including those for which indexing is not supported (e.g. generator) or slow (e.g. collections.deque).
You could use a zip.
>>> list(zip(range(5), range(2, 6)))
[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]
Just like a zipper, it creates pairs. So, to to mix your two lists, you get:
>>> l = [1,7,3,5]
>>> list(zip(l[:-1], l[1:]))
[(1, 7), (7, 3), (3, 5)]
Then iterating goes like
for x, y in zip(l[:-1], l[1:]):
pass
If you wanted something inline but not terribly readable here's another solution that makes use of generators. I expect it's also not the best performance wise :-/
Convert list into generator with a tweak to end before the last item:
gen = (x for x in l[:-1])
Convert it into pairs:
[(gen.next(), x) for x in l[1:]]
That's all you need.
I have list:
numbers = [2,3,1,6,5]
And I must remove one min and one max number:
sorted(numbers)[1:-1]
And this is ok, but I want get additional information - position of removed numbers in original list:
remains = sorted(numbers)[1:-1]
min_number_position = 2
max_number_position = 3
How to do it? Numbers can be repeated.
Just use min and max functions in couple with index method of list to get position:
min_position = numbers.index(min(numbers))
max_position = numbers.index(max(numbers))
del numbers[min_position]
del numbers[max_position]
A pure python solution by creating arg sorted array (as created by numpy.argsort()) . Example -
numbers = [2,3,1,6,5]
argsorted = sorted(range(len(numbers)),key=lambda x:numbers[x])
maxpos,minpos = argsorted[-1],argsorted[0]
remains = [numbers[i] for i in argsorted[1:-1]]
Demo -
>>> numbers = [2,3,1,6,5]
>>> argsorted = sorted(range(len(numbers)),key=lambda x:numbers[x])
>>> argsorted
[2, 0, 1, 4, 3]
>>> maxpos,minpos = argsorted[-1],argsorted[0]
>>> remains = [numbers[i] for i in argsorted[1:-1]]
>>> remains
[2, 3, 5]
>>> maxpos
3
>>> minpos
2
If you can use numpy library, this can be easily done using array.argsort() . Example -
nnumbers = np.array(numbers)
nnumargsort = nnumbers.argsort()
minpos,maxpos = nnumargsort[[0,-1]]
remains = nnumbers[nnumargsort[1:-1]]
Demo -
In [136]: numbers = [2,3,1,6,5]
In [137]: nnumbers = np.array(numbers)
In [138]: nnumargsort = nnumbers.argsort()
In [139]: minpos,maxpos = nnumargsort[[0,-1]]
In [140]: remains = nnumbers[nnumargsort[1:-1]]
In [141]: remains
Out[141]: array([2, 3, 5])
In [142]: maxpos
Out[142]: 3
In [143]: minpos
Out[143]: 2
>>> sorted(enumerate(numbers), key=operator.itemgetter(1))
[(2, 1), (0, 2), (1, 3), (4, 5), (3, 6)]
The rest is left as an exercise for the reader.
You can use a function and return the index of max and min with list.index method :
>>> def func(li):
... sorted_li=sorted(li)
... return (li.index(sorted_li[0]),sorted_li[1:-1],li.index(sorted_li[-1]))
...
>>> min_number_position,remains,max_number_position=func(numbers)
>>> min_number_position
2
>>> remains
[2, 3, 5]
>>> max_number_position
3
In python 3.X you can use unpacking assignment :
>>> def func(li):
... mi,*re,ma=sorted(li)
... return (li.index(mi),re,li.index(ma))
I would like to limit for loop to print first few element from list in terms of their value. For example, if i < 6 :
list = [1,2,3,4,5,6,7,8,9,10]
for i < 6 in list:
print(i)
Thanks in advance !
In [9]: L = [1,2,3,4,5,6,7,8,9,10]
In [10]: for i in L:
....: if i<6:
....: print(i)
....:
1
2
3
4
5
based on I would like to limit for loop to print first few element from list in terms of their value it seems the list is in order so you can use itertools.takewhile :
from itertools import takewhile
lst = [1,2,3,4,5,6,7,8,9,10] # don't use list
tke = takewhile(lambda x: x< 6, lst)
for t in tke:
print(t)
1
2
3
4
5
If you want a list use list(...).
print(list(takewhile(lambda x: x< 6, lst))) # good reason why we should not use list as a variable name
[1, 2, 3, 4, 5]
This question already has answers here:
Rolling or sliding window iterator?
(29 answers)
Closed 8 years ago.
I have a generator, let's say:
x = iter([1,2,3,4,5,6])
And if i want to loop 3 items at a time but step only 1 each time, i want to get:
1 2 3
2 3 4
3 4 5
5 6
I have tried looping two at a time but it steps 2 each time:
x = iter([1,2,3,4,5,6])
x = list(x)
for i,j in zip(x[::2],x[1::2]):
print i,j
[out]:
1 2
3 4
5 6
I have tried looping n at a time but it also steps n:
from itertools import izip_longest
def grouper(n, iterable, fillvalue=None):
args = [iter(iterable)] * n
return izip_longest(fillvalue=fillvalue, *args)
x = iter([1,2,3,4,5,6])
for i,j in grouper(3,x):
print i,j
print
[out]:
Traceback (most recent call last):
File "test.py", line 11, in <module>
for i,j in grouper(3,x):
ValueError: too many values to unpack
I have tried just access +n each time i loop:
x = iter([1,2,3,4,5,6])
x = list(x)
for i,j in enumerate(x):
print x[i], x[i+1], x[i+2]
[out]:
1 2 3
2 3 4
3 4 5
4 5 6
5 6
Traceback (most recent call last):
File "test.py", line 26, in <module>
print x[i], x[i+1], x[i+2]
IndexError: list index out of range
Question:
Can I not change the generator into a list before looping and accessing more than one value at a time but stepping only 1 each time?
Other than using the last solution i have, how to else achieve the desired iteration?
You can use tee, islice and izip_longest as a base, and adapt the "stop" condition, eg:
from itertools import tee, islice, izip_longest
data = [1, 2, 3, 4, 5, 6]
iters = [islice(it, n, None) for n, it in enumerate(tee(data, 3))]
res = list(izip_longest(*iters))
# [(1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6), (5, 6, None), (6, None, None)]
An example stop condition might be:
from itertools import takewhile
res = list(takewhile(lambda L: None not in L, izip_longest(*iters)))
# [(1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6)]