I am trying to calculate rolling on hand inventory in power query at a week level for each location and product type.
In general the formula is:
Starting Inventory (System Data) + Receipts (Forecast) - Shipments (Forecast) + Returns (Forecast) = Ending Inventory (Forecast)
The format of the forecast data after pivoting Shipments, Receipts and Returns is:
Location, Product Type, Week, Shipments, Receipts, Returns
A, B, 1, 54, 69, 8
A, B, 2, 98, 12, 3
A, B, 3, 68, 50, 3
A, C, 1, 9, 58, 9
A, C, 2, 95, 20, 5
A, C, 3, 93, 42, 10
D, B, 1, 27, 87, 7
D, B, 2, 5, 2, 4
D, B, 3, 92, 19, 4
D, C, 1, 96, 17, 5
D, C, 2, 50, 50, 7
D, C, 3, 70, 95, 4
The format of the starting inventory is:
Location, Product Type, Starting Inventory
A, B, 116
A, C, 117
D, B, 108
D, C, 197
The desired result is:
Location, Product Type, Week, Shipments, Receipts, Returns, Ending Inventory
A, B, 1, 54, 69, 8, 139
A, B, 2, 98, 12, 3, 56
A, B, 3, 68, 50, 3, 41
A, C, 1, 9, 58, 9, 175
A, C, 2, 95, 20, 5, 105
A, C, 3, 93, 42, 10, 64
D, B, 1, 27, 87, 7, 175
D, B, 2, 5, 2, 4, 176
D, B, 3, 92, 19, 4, 107
D, C, 1, 96, 17, 5, 123
D, C, 2, 50, 50, 7, 130
D, C, 3, 70, 95, 4, 159
My internet searches make me think it would be done best with List.Generate, but I am not finding clear enough examples for me to follow.
So far I have only exported to excel and done the calculation there.
Any help would be greatly appreciated.
Try the code below.
Read the code comments and explore the Applied steps to better understand the algorithm
let
// Change Source= and Source2= lines to reflect the actual table names of the respective tables
//Read in the Inventory table
Source = Excel.CurrentWorkbook(){[Name="Inventory"]}[Content],
Inventory = Table.TransformColumnTypes(Source,{
{"Location", type text}, {"Product Type", type text}, {"Starting Inventory", Int64.Type}}),
//Read in the Data table
Source2 = Excel.CurrentWorkbook(){[Name="Data"]}[Content],
Data = Table.TransformColumnTypes(Source2,{
{"Location", type text}, {"Product Type", type text}, {"Week", Int64.Type}, {"Shipments", Int64.Type},
{"Receipts", Int64.Type}, {"Returns", Int64.Type}}),
// "Lookup" Starting inventory for each Location/Product type
// by doing a Table.Join
#"Join Starting Inventory" = Table.NestedJoin(Data,{"Location","Product Type"}, Inventory,{"Location","Product Type"},
"Joined", JoinKind.LeftOuter),
//Extract only the starting inventory column
#"Expanded Joined" = Table.ExpandTableColumn(#"Join Starting Inventory", "Joined", {"Starting Inventory"}, {"Starting Inventory"}),
//Group by Location and Product Type
//Then use List.Generate to calculate the Running Ending Inventory for each Location/Product type
#"Grouped Rows" = Table.Group(#"Expanded Joined", {"Location", "Product Type"}, {
{"Calc", (t)=>
let
Ending = List.Generate(
()=>[e=t[Starting Inventory]{0} + t[Receipts]{0} - t[Shipments]{0} + t[Returns]{0}, idx=0],
each [idx]< Table.RowCount(t),
each [e=[e] + t[Receipts]{[idx]+1} - t[Shipments]{[idx]+1} + t[Returns]{[idx]+1}, idx=[idx]+1],
each [e])
in
//Add the Ending Inventory List as a new column
// while removing the Starting Inventory column
Table.FromColumns(
Table.ToColumns(Table.RemoveColumns(t,"Starting Inventory")) & {Ending},
Table.ColumnNames(Data) & {"Ending Inventory"})}}),
//Expand the Grouped tables
#"Expanded Calc" = Table.ExpandTableColumn(#"Grouped Rows", "Calc",
List.RemoveFirstN(Table.ColumnNames(Data),2) & {"Ending Inventory"}),
//Set data types
typeIt = Table.TransformColumnTypes(#"Expanded Calc",
List.Transform(List.RemoveFirstN(Table.ColumnNames(#"Expanded Calc"),2), each {_, Int64.Type}))
in
typeIt
Related
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]].
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>
I have recently started to work with python 2.7 .
i have some data which i am passing to product API of amazon, to make it a batch call i want to pass 10 values per call as that is the max IDs or keywords per batch call.
Here is a question that, how to pass only 10 values to the function. I have some 76 values(it may increase) in total which is a list and 6 at the end.I can read values from list using *args but to get only 10 values how can i process it using for-loop statement or any loop.
I want to do something like this
data = rows_db
count = 76
for id in data[range start , count ]:
ids = id #copy 10 values or less
foo(ids)
start = start + 10
def foo(*ids):
#process and retrieve values
I guess you want to do something like this:
data_copy = list(data) # you can replace any appearance of data_copy with data if you don't care if it is changed
while data_copy: # this is equivalent to: while len(data_copy) != 0:
to = min(10, len(data_copy)) # If there are less then 10 entries left, the length will be smaller than ten, so that it is either 10 or the (smaller) length. This is the amount of data that's processed
f(data_copy[:to]) # make the function call with any value up to 'to'
del data_copy[:to] # delete the data, because we already processed it
This:
def f(x): print(x)
data = list(range(53)) # list from 0 (included) to 52 (included)
# here is the top part
yields the expected output of
[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]
I have df1:
id, colA, colB, colC, name
1, 1, 2, 3, a
2, 2, 3, 4, a
3, 3, 4, 5, b
4, 4, 5, 6, b
and df2:
id, colA, colB, colD, name
2, 10, 20, D1, a
3, 20, 30, D2, a
Is there a way, perhaps using merge or join to replace the rows in df with df2 matching id and name
So the result would look like:
id, colA, colB, colC, name, colD
1, 1, 2, 3, a, N/A
2, 10, 20, N/A, a, D1
3, 3, 4, 5, b, N/A
4, 4, 5, 6, b, N?A
I was thinking something like: df1.loc[df1.Locident.isin(df2.Locident)] = df2 but that only matches on one column.
You could:
df = pd.concat([df1, df2]).drop_duplicates(subset=['id', 'name'], keep='last').drop_duplicates(subset='id')
to combine both DataFrames and keep duplicate ids and names that stem from df2, and get rid of ids from df2 that you do not want to keep.
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.