python combining a range and a list of numbers - python

range(5, 15) [1, 1, 5, 6, 10, 10, 10, 11, 17, 28]
range(6, 24) [4, 10, 10, 10, 15, 16, 18, 20, 24, 30]
range(7, 41) [9, 18, 19, 23, 23, 26, 28, 40, 42, 44]
range(11, 49) [9, 23, 24, 27, 29, 31, 43, 44, 45, 45]
range(38, 50) [1, 40, 41, 42, 44, 48, 49, 49, 49, 50]
I get the above outpout from a print command from a function. What I really want is a combined list of the range, for example in the top line 5,6,7...15,1,1,5,6 etc.
The output range comes from
range_draws=range(int(lower),int(upper))
which I naively thought would give a range. The other numbers come from a sliced list.
Could someone help me to get the desired result.

The range() function returns a special range object to save on memory (no need to keep all the numbers in memory when only the start, end and step size will do). Cast it to a list to 'expand' it:
list(yourrange) + otherlist
To quote the documentation:
The advantage of the range type over a regular list or tuple is that a range object will always take the same (small) amount of memory, no matter the size of the range it represents (as it only stores the start, stop and step values, calculating individual items and subranges as needed).

Related

Nested dict loop

I just wrote some code the should simulate parcel orders for a series of 90 day,
This part makes csv files for all order days:
Period = list(range(1,90))
for item in Preiod:
"some code to simulate the days, which is irrelevant here"
.to_csv(rf'file_location_Day{item}.csv)
I then use a solver to make routes for the vehicles that deliver the parcels. this solver opens the csv file, and gives an output like this(nested list):
[[36, 27, 11, 15, 41, 35, 20, 42, 24, 21, 37, 5, 25, 39, 12, 31], [3, 34, 32, 33, 47, 9, 16, 13, 28, 22, 17, 43, 7, 45, 38], [6, 30, 1, 40, 4, 8, 46, 23, 14, 18, 29, 44, 10, 26, 2, 19]]
In this case 3 lists: vehicle 1 delivers parcel 36 then 27 then 11, vehicle 2 delivers parcel 3 then 34, then 32 etc. This nested list is stored as results.routs
I would like to store this data in a nested dictionary, i think this is allready close:
Routes = {}
Routes[Day1] = result.routes
But i'd like an extra level:
Routes[Day1],[vehicle1] =
Routes[Day1],[vehicle1], [ParcelNumber, x-coordinate, y-coordinate] =
This is because i will later need to search the coordinates for all corresponding parcel locations. (bit like vlookup in excel)
e.g. in the csv file search the 36th parcel location, pick the x and y coordinate in the two columns next to it, and then paste it in the corresponding lists in the dict.
My biggest challange: How do i make a loop for this in a neat way

How to expand hyphenated numbers from a file into a range of numbers?

I have a text file formatted like so
1-8
10-12
14-45
48-50
How do I go about getting the range of each line?
1, 2, 3, 4, 5, 6, 7, 8
10, 11, 12
I've tried splitting the file to get each number into a list. 1, 8, 10, 12, 14, 45, 48, 50, but I'm not sure how to translate that into the start and end numbers for range.
data = []
with open('file.txt','r') as myfile:
for line in myfile:
data.extend(map(int, line.split('-')))
print (data)
You can use a short list comprehension to extract the numbers from a given line. Since the ranges you listed seem to be inclusive on both ends, we need to add one to end interval (j) below.
for line in myfile:
i, j = [int(n) for n in line.split("-")]
for x in range(i, j + 1):
# do things
If you simply want to stick the two numbers in a range, here's an alternative:
range(*[int(n) for n in line.split("-")])
If you want to list out the numbers in the range, you need to wrap the expression in a list().
You just need to extract the start and end index from each line and use it in range to create your list
data = []
with open('file.txt','r') as myfile:
for line in myfile:
start,end = [int(item) for item in line.split('-')]
li = list(range(start ,end+1))
data.append(li)
print(data)
So if the input is:
1-8
10-12
14-45
48-50
The output will be:
[
[1, 2, 3, 4, 5, 6, 7, 8],
[10, 11, 12],
[14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45],
[48, 49, 50]
]
If you just want to print the ranges, you can use this:
with open('file.txt', 'r') as myfile:
for line in myfile:
nums = [int(i) for i in line.split('-')]
nums[1] += 1 # more on this in a second
my_range = range(*nums) # the * unpacks the two numbers into two arguments
print([i for i in my_range])
# [1, 2, 3, 4, 5, 6, 7, 8]
Walking through it, the first line in the for loop you pretty much have already. This just grabs the two numbers and interprets them as integers. After this line, nums = [1, 8] for your first line '1-8'. Next we add one to the the last element of the list, so nums = [1, 9] now. We do this because the range(a, b) builtin generates numbers from a to b-1.
Next we create a range that will generate the numbers that you want with range(*nums). The * in that statement unpacks the list into the two arguments that range() is expecting.
Finally we print all of the items in the range. Since my_range is now a generator, we need to unpack it to print, so we use a list comprehension to iterate over it and get all of the numbers.
You're very close, you just need to append the list range():
data = []
with open("file.txt") as f:
for line in f:
start, end = map(int, line.split("-"))
data.append(list(range(start, end + 1)))
print(data)
# [[1, 2, 3, 4, 5, 6, 7, 8], [10, 11, 12], [14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45], [48, 49, 50]]
And also making sure the end is incremented by one because it is not inclusive in range().
How about something like this?
Of course, it has no error checking, so you'd want to be mindful of the quality of your input data.
data = []
with open('file.txt','r') as myfile:
for line in myfile:
# Split the line and force conversion to int
start_int, end_int = map(int, line.split('-'))
# Get a Python 3 range - note this is not a list but a "range" type in Python 3, so we'll have to convert it before appending to our global list
int_range = range(start_int, end_int+1)
data.append(list(int_range))
I believe that you can match the pattern using capturing groups using Regex and add it into your list.
Regex: https://regex101.com/r/f1Ghff/1
(\d+)\-(\d+)
Explanation:
1st Capturing Group (\d+)
\d+ matches a digit (equal to [0-9])
+ Quantifier — Matches between one and unlimited times, as many times as possible, giving back as needed (greedy)
\- matches the character - literally (case sensitive)
2nd Capturing Group (\d+)
\d+ matches a digit (equal to [0-9])
+ Quantifier — Matches between one and unlimited times, as many times as possible, giving back as needed (greedy)
The code:
import re
str = """
1-8
10-12
14-45
48-50
"""
pattern = r"(\d+)\-(\d+)"
matches = re.findall(pattern, str)
ranges = []
# Convert Group1 and Group2 into integers
for tuple in matches:
low = int(tuple[0])
high = int(tuple[1])
ranges.append(list(range(low, high)))
print (ranges)
Outputting:
[[1, 2, 3, 4, 5, 6, 7], [10, 11], [14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44], [48, 49]]

Print list in specified range Python

I'm new to Python and I have this problem
I have a list of numbers like this:
n = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
I want to print from 11 to 37, that means the output = 11, 13,.... 37.
I tried to print(n[11:37]) but of course it will print [37, 41, 43, 47]
because that is range index.
Any ideas or does Python have any built-in method for this ?
This should do the job...
n = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
n.sort()
mylist = [x for x in n if x in range(11, 38)]
print(mylist)
Want to print that as comma separated string:
print(mylist.strip('[]'))
This will work. (Assuming list is sorted)
print n[n.index(11): n.index(37)+1]
Output:
[11, 13, 17, 19, 23, 29, 31, 37]
Considering your list is ordered and it has no duplicates:
n = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
print(",".join(map(str,n[n.index(11): n.index(37)+1])))
Here you have a live example
Using numpy:
import numpy as np
narr = np.array(n)
m = (narr >= 11) & (narr <= 37)
for v in narr[m]:
print(v)
# or, to get rid of the loop:
print('\n'.join(map(str, narr[m])))
it pretty simple, since your list is already sorted you can write
my_list = [x for x in n if x in range(11, 38)]
print(*my_list)
what the '*' does is that it unpacks the array into individual elements, a term known as unpacking.This will produce the actual result you wanted and not an array
If your data is sorted, you can use a generator expression with either a range object or chained comparisons:
n = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
print(*(i for i in n if i in range(11, 38)), sep=', ')
print(*(i for i in n if 11 <= i <= 37), sep=', ')
If your data is unsorted and you can use indices of the first occurrences of each value, you can slice your list:
print(*n[n.index(11): n.index(37)+1], sep=', ')
Result with the data you have provided:
11, 13, 17, 19, 23, 29, 31, 37

Periodically slice an list/array

Suppose i have a a = range(1,51). How can i slice a to create a new list that look like this:
[1,2,3,11,12,13,21,22,23,31,32,33,41,42,43]
Is there a pythonic way that can help me do this without writing function?
I know that [start:stop:step] for periodically slicing one element but i'm not sure if i'm missing something obvious.
EDIT: The suggested duplicate question/answer is not the same as mine question. I simply asked to slice/extract periodically elements from a larger list/array. The suggested duplicate modifies elements of existing array.
Another option you can go with logical vector subsetting, something like:
a[(a - 1) % 10 < 3]
# array([ 1, 2, 3, 11, 12, 13, 21, 22, 23, 31, 32, 33, 41, 42, 43])
(a - 1) % 10 finds the remainder of array by 10 (period); and (a - 1) % 10 < 3 gives a logical vector which gives true for the first three elements of every ten elements.
What you want is more complicated than a simple slice, so you're going to need some kind of (likely fairly simple) function to do it. I'd look at using zip to combine multiple slices, something like:
reduce(lambda a,b:a+b, map(list, zip(a[1::10], a[2::10], a[3::10])))
Given:
>>> li=range(1,52)
You can do:
>>> [l for sl in [li[i:i+3] for i in range(0,len(li),10)] for l in sl]
[1, 2, 3, 11, 12, 13, 21, 22, 23, 31, 32, 33, 41, 42, 43, 51]
Or, if you want only full sublists:
>>> [l for sl in [li[i:i+3] for i in range(0,len(li),10)] for l in sl if len(sl)==3]
[1, 2, 3, 11, 12, 13, 21, 22, 23, 31, 32, 33, 41, 42, 43]
Or, given:
>>> li=range(1,51)
Then you do not need to test sublists:
>>> [l for sl in [li[i:i+3] for i in range(0,len(li),10)] for l in sl]
[1, 2, 3, 11, 12, 13, 21, 22, 23, 31, 32, 33, 41, 42, 43]
Psidom's answer's index math can be adapted to a list comprehension too
a = range(1,51)
[n for n in a if (n - 1) % 10 < 3]
Out[23]: [1, 2, 3, 11, 12, 13, 21, 22, 23, 31, 32, 33, 41, 42, 43]

fast categorization (binning)

I've a huge number of entries, every one is a float number. These data x are accesible with an iterator. I need to classify all the entries using selection like 10<y<=20, 20<y<=50, .... where y are data from an other iterables. The number of entries is much more than the number of selections. At the end I want a dictionary like:
{ 0: [all events with 10<x<=20],
1: [all events with 20<x<=50], ... }
or something similar. For example I'm doing:
for x, y in itertools.izip(variable_values, binning_values):
thebin = binner_function(y)
self.data[tuple(thebin)].append(x)
in general y is multidimensional.
This is very slow, is there a faster solution, for example with numpy? I think the problem cames from the list.append method I'm using and not from the binner_function
A fast way to get the assignments in numpy is using np.digitize:
http://docs.scipy.org/doc/numpy/reference/generated/numpy.digitize.html
You'd still have to split the resulting assignments up into groups. If x or y is multidimensional, you will have to flatten the arrays first. You could then get the unique bin assignments, and then iterate over those in conjunction with np.where to split the the assigments up into groups. This will probably be faster if the number of bins is much smaller than the number of elements that need to be binned.
As a somewhat trivial example that you will need to tweak/elaborate on for your particular problem (but is hopefully enough to get you started with with a numpy solution):
In [1]: import numpy as np
In [2]: x = np.random.normal(size=(50,))
In [3]: b = np.linspace(-20,20,50)
In [4]: assign = np.digitize(x,b)
In [5]: assign
Out[5]:
array([23, 25, 25, 25, 24, 26, 24, 26, 23, 24, 25, 23, 26, 25, 27, 25, 25,
25, 25, 26, 26, 25, 25, 26, 24, 23, 25, 26, 26, 24, 24, 26, 27, 24,
25, 24, 23, 23, 26, 25, 24, 25, 25, 27, 26, 25, 27, 26, 26, 24])
In [6]: uid = np.unique(assign)
In [7]: adict = {}
In [8]: for ii in uid:
...: adict[ii] = np.where(assign == ii)[0]
...:
In [9]: adict
Out[9]:
{23: array([ 0, 8, 11, 25, 36, 37]),
24: array([ 4, 6, 9, 24, 29, 30, 33, 35, 40, 49]),
25: array([ 1, 2, 3, 10, 13, 15, 16, 17, 18, 21, 22, 26, 34, 39, 41, 42, 45]),
26: array([ 5, 7, 12, 19, 20, 23, 27, 28, 31, 38, 44, 47, 48]),
27: array([14, 32, 43, 46])}
For dealing with flattening and then unflattening numpy arrays, see:
http://docs.scipy.org/doc/numpy/reference/generated/numpy.unravel_index.html
http://docs.scipy.org/doc/numpy/reference/generated/numpy.ravel_multi_index.html
np.searchsorted is your friend. As I read somewhere here in another answer to the same topic, it's currently a good bit faster than digitize, and does the same job.
http://docs.scipy.org/doc/numpy/reference/generated/numpy.searchsorted.html

Categories

Resources