sorry I am new to Prolog and logic programming. I was wondering if the following is possible in Prolog:
Given j lists of size n = k*j, how do I rearrange them into m lists, each containing the first k elements of each of the j lists?
For example, given a list of lists of 12 elements, such as
[
[ 1, 2, 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 ],
[ 13, 14, 15 , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 ],
[ 25, 26, 27 , 28 , 29 , 30 , 31 , 32 , 33 , 34 , 35 , 36 ]
]
How do I transform it to
[
[ 1, 2, 3, 4, 13, 14, 15, 16, 25, 26, 27, 28 ],
[ 5, 6, 7, 8, 17, 18, 19, 20, 29, 30, 31, 32 ],
[ 9, 10, 11, 12, 21, 22, 23, 24, 33, 34, 35, 36 ]
]
???
I can extract the first k elements of each list in the list.
getFirstK(List, K, FirstK, Remainder) :-
length(FirstK, K),
append(FirstK, Remainder, List).
And I thought I could get at least [1,2,3,4,13,14,15,16,25,26,27,28] with the following,
GetLists([], K, []).
GetLists([FirstList|RestOfLists], K, Result) :-
getFirstK(FirstList, K, FirstK, Remainder),
GetLists(RestOfLists, K, [FirstK|Result]).
However, when I run getLists to get Result, I get false instead. Is there a way to get the list of lists?
You can write a procedure that takes the first K elements of a list, then append all the resulting lists and recursively apply this procedure until there are only empty lists in your input:
get_lists(LL, _, []):-
maplist(=([]), LL).
get_lists(LL, K, [R|LR]):-
maplist(split(K), LL, LChunks, LRest),
append(LChunks, R),
get_lists(LRest, K, LR).
split(K, L, Chunk, Rest):-
length(Chunk, K),
append(Chunk, Rest, L).
Sample run:
?- get_lists( [[1,2,3,4,5,6,7,8,9,10,11,12], [13,14,15,16,17,18,19,20,21,22,23,24],[25,26,27,28,29,30,31,32,33,34,35,36]], 4, LR).
LR = [[1, 2, 3, 4, 13, 14, 15, 16, 25, 26, 27, 28], [5, 6, 7, 8, 17, 18, 19, 20, 29, 30, 31, 32], [9, 10, 11, 12, 21, 22, 23, 24, 33, 34, 35, 36]] ;
false.
Using difference lists, for performance:
rearrange_lists([H|T], BiteLen, RLs) :-
% Populate the heads, leaving the tails
spread_list(H, BiteLen, RLs, Tails),
% Loop through populating the tails
rearrange_lists_(T, BiteLen, Tails).
rearrange_lists_([], _, Tails) :-
% Close the tails
maplist(=([]), Tails).
rearrange_lists_([H|T], BiteLen, Heads) :-
spread_list(H, BiteLen, Heads, Tails),
rearrange_lists_(T, BiteLen, Tails).
spread_list([], _BL, [], []).
spread_list([H|T], BiteLen, [BiteH|BiteHs], [BiteT|BiteTs]) :-
copy_list_to_dl_len(BiteLen, [H|T], _HS, HR, BiteH, BiteT),
spread_list(HR, BiteLen, BiteHs, BiteTs).
% Generic, reusable predicate
copy_list_to_dl_len(Len, Lst, LstH, LstT, SubLstH, SubLstT) :-
( nonvar(Len)
-> integer(Len),
Len #>= 0,
% Only once
copy_list_to_dl_len_inc_(Lst, LstH, LstT, 0, Len, SubLstH, SubLstT), !
; copy_list_to_dl_len_inc_(Lst, LstH, LstT, 0, Len, SubLstH, SubLstT)
).
copy_list_to_dl_len_inc_(Lst, Lst, Lst, Len, Len, SubLst, SubLst).
copy_list_to_dl_len_inc_([H|T], [H|HT], LT, Len, FLen, [H|ST], SLT) :-
Len1 is Len + 1,
copy_list_to_dl_len_inc_(T, HT, LT, Len1, FLen, ST, SLT).
Result in swi-prolog:
?- time(rearrange_lists([[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], [13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24], [25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36]], 4, RLs)).
% 87 inferences, 0.000 CPU in 0.000 seconds (95% CPU, 1429182 Lips)
RLs = [[1, 2, 3, 4, 13, 14, 15, 16, 25, 26, 27, 28], [5, 6, 7, 8, 17, 18, 19, 20, 29, 30, 31, 32], [9, 10, 11, 12, 21, 22, 23, 24, 33, 34, 35, 36]].
Related
I have a list of 51 elements.
keypoints = ['0.49395501613616943', '0.3686272203922272', '0.9999948740005493'...]. The original index of my list goes in order from 0,1,2...50. But i have to set new indices to the elements of the list and order them accordingly in a new list. How can i do that?
My new indices:
NEW_INDEX = [39, 40, 41, 21, 22, 23,
27, 28, 29, 33, 34, 35, 24, 25, 26,
30, 31, 32, 36, 37, 38, 42, 43, 44, 45, 46, 47,
48, 49, 50, 3, 4, 5, 9, 10, 11, 15, 16, 17,
6, 7, 8, 12, 13, 14, 18, 19, 20, 0, 1, 2]
Assuming this question is about python syntax.
In pure python, you can use a list comprehension and indices:
keypoints = ['0.49395501613616943', '0.3686272203922272', '0.9999948740005493', '0.9473770229920709', '0.7699773520016487', '0.540962426318277']
new_index = [2, 1, 5, 4, 0, 3]
ordered_keypoints = [keypoints[i] for i in new_index]
print(ordered_keypoints)
# ['0.9999948740005493', '0.3686272203922272', '0.540962426318277', '0.7699773520016487', '0.49395501613616943', '0.9473770229920709']
Using numpy, there is an even more convenient notation:
import numpy as np
keypoints = np.array(['0.49395501613616943', '0.3686272203922272', '0.9999948740005493', '0.9473770229920709', '0.7699773520016487', '0.540962426318277'])
new_index = np.array([2, 1, 5, 4, 0, 3])
ordered_keypoints = keypoints[new_index]
print(ordered_keypoints)
# ['0.9999948740005493' '0.3686272203922272' '0.540962426318277'
# '0.7699773520016487' '0.49395501613616943' '0.9473770229920709']
I am browsing through mir.ndslice docs trying to figure out how to do a simple row selection by column.
In numpy I would do:
a = np.random.randint(0, 20, [4, 6])
# array([[ 8, 5, 4, 18, 1, 4],
# [ 2, 18, 15, 7, 18, 19],
# [16, 5, 4, 6, 11, 11],
# [15, 1, 14, 6, 1, 4]])
a[a[:,2] > 10] # select rows where the second column value is > 10
# array([[ 2, 18, 15, 7, 18, 19],
# [15, 1, 14, 6, 1, 4]])
Using mir library I naively tried:
import std.range;
import std.random;
import mir.ndslice;
auto a = generate!(() => uniform(0, 20)).take(24).array.sliced(4,6);
// [[12, 19, 3, 10, 19, 11],
// [19, 0, 0, 13, 9, 1],
// [ 0, 0, 4, 13, 1, 2],
// [ 6, 19, 14, 18, 14, 18]]
a[a[0..$,2] > 10];
But got
Error: incompatible types for `((ulong __dollar = a.length();) , a.opIndex(a.opSlice(0LU, __dollar), 2)) > (10)`: `Slice!(int*, 1LU, cast(mir_slice_kind)0)` and `int`
dmd failed with exit code 1.
So, I went through the docs and couldn't find anything that would look like np.where or similar. Is it even possible in mir?
Say I have the below-mentioned list:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
Now, if user wants 4 sub-lists (n=4), then the sub-lists will be
[0,1,2,3,4,5]
[6,7,8,9,10,11]
[12,13,14,15,16,17]
[18,19,20]
Similarly, if user wants 6 sub-lists (n=6), then the sub-lists will be
[0,1,2,3]
[4,5,6,7]
[8,9,10,11]
[12,13,14,15]
[16,17,18,19]
[20]
Please let me know how can I achieve this.
list1 =[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
n=6
k = int(len(list1)/float(n))+1
i=0
for x in range(n-1):
i=(x+1)*k
print list1[i-k:i]
print list1[i:]
Try this python code. It answers your problem perfectly.
I have data frame in pandas and I have written a function to use the information in each row to generate a new column. I want the result to be in a list format:
A B C
3 4 1
4 2 5
def Computation(row):
if row['B'] >= 3:
return [s for s in range(row['C'],50)]
else:
return [s for s in range(row['C']+2,50)]
df['D'] = df.apply(Computation, axis = 1)
However, I am getting the following error:
"could not broadcast input array from shape (308) into shape (9)"
Could you please tell me how to solve this problem?
Say you start with
In [25]: df = pd.DataFrame({'A': [3, 4], 'B': [4, 2], 'C': [1, 5]})
Then there are at least two ways to do it.
You can apply twice on the C column, but switch on the B column:
In [26]: np.where(df.B >= 3, df.C.apply(lambda c: [s for s in range(c, 50)]), df.C.apply(lambda c: [s for s in range(c + 2, 50)]))
Out[26]:
array([ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 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, 46, 47, 48, 49],
[7, 8, 9, 10, 11, 12, 13, 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, 46, 47, 48, 49]], dtype=object)
Or you can apply on the entire row and switch on the B value per row:
In [27]: df.apply(lambda r: [s for s in range(r.C, 50)] if r.B >= 3 else [s for s in range(r.C + 2, 50)], axis=1)
Out[27]:
0 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14...
1 [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, ...
Note that the return types are different, but, in each case, you can still write
df['foo'] = <each one of the above options>
Is it possible to simulate extended tuple unpacking in Python 2?
Specifically, I have a for loop:
for a, b, c in mylist:
which works fine when mylist is a list of tuples of size three. I want the same for loop to work if I pass in a list of size four.
I think I will end up using named tuples, but I was wondering if there is an easy way to write:
for a, b, c, *d in mylist:
so that d eats up any extra members.
You can't do that directly, but it isn't terribly difficult to write a utility function to do this:
>>> def unpack_list(a, b, c, *d):
... return a, b, c, d
...
>>> unpack_list(*range(100))
(0, 1, 2, (3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 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, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99))
You could apply it to your for loop like this:
for sub_list in mylist:
a, b, c, d = unpack_list(*sub_list)
You could define a wrapper function that converts your list to a four tuple. For example:
def wrapper(thelist):
for item in thelist:
yield(item[0], item[1], item[2], item[3:])
mylist = [(1,2,3,4), (5,6,7,8)]
for a, b, c, d in wrapper(mylist):
print a, b, c, d
The code prints:
1 2 3 (4,)
5 6 7 (8,)
For the heck of it, generalized to unpack any number of elements:
lst = [(1, 2, 3, 4, 5), (6, 7, 8), (9, 10, 11, 12)]
def unpack(seq, n=2):
for row in seq:
yield [e for e in row[:n]] + [row[n:]]
for a, rest in unpack(lst, 1):
pass
for a, b, rest in unpack(lst, 2):
pass
for a, b, c, rest in unpack(lst, 3):
pass
You can write a very basic function that has exactly the same functionality as the python3 extended unpack. Slightly verbose for legibility. Note that 'rest' is the position of where the asterisk would be (starting with first position 1, not 0)
def extended_unpack(seq, n=3, rest=3):
res = []; cur = 0
lrest = len(seq) - (n - 1) # length of 'rest' of sequence
while (cur < len(seq)):
if (cur != rest): # if I am not where I should leave the rest
res.append(seq[cur]) # append current element to result
else: # if I need to leave the rest
res.append(seq[cur : lrest + cur]) # leave the rest
cur = cur + lrest - 1 # current index movded to include rest
cur = cur + 1 # update current position
return(res)
Python 3 solution for those that landed here via an web search:
You can use itertools.zip_longest, like this:
from itertools import zip_longest
max_params = 4
lst = [1, 2, 3, 4]
a, b, c, d = next(zip(*zip_longest(lst, range(max_params))))
print(f'{a}, {b}, {c}, {d}') # 1, 2, 3, 4
lst = [1, 2, 3]
a, b, c, d = next(zip(*zip_longest(lst, range(max_params))))
print(f'{a}, {b}, {c}, {d}') # 1, 2, 3, None
For Python 2.x you can follow this answer.