Loop through an array in a single worksheet cell - arrays

I am trying to calculate an amount based on a formula that has a different number of arguments for each calculation. And, each formula is expressed as a string that is based on the column names at the top of the sheet (e.g. item1). Each argument feeds a XLOOKUP to get the value and then that value gets added to the one before it (a+b+c+d).
|item1|item2|item3|item4|item5|
itemAmount 100 50 25 2 3
rate ? ? ? ? ?
formula for item1 rate = item1
formula for item2 rate = item1, item2
formula for item3 rate = item3
formula for item4 rate = item3, item4
formulas are expressed in a comma-separated array in a cell in the worksheet.
Desired Results:
rate for item1 = 100
rate for item2 = 150
rate for item3 = 25
rate for item4 = 27
rate for item5 = 180
The below works until I have 4 or more arguments to add together.
e.g. item1, item2, item3, item4 will not get calculated.
Is there a way to implement this that truly loops through the formula arrays?
I tried REDUCE() but that didn't seem to recognize text.
What I have so far is a combination of LAMBDA() and LET().
I used LAMBDA() to create a reusable formula for the users that will get inserted in dozens of places in the workbook.
=LAMBDA(
TaskFormula,TaskNamesRange,RateName,RateNamesRange,ReturnRange,
LET(
tfl,
TaskFormula,
tnrng,
TaskNamesRange,
rateType,
TRIM(RIGHT(RateName,LEN(RateName)-FIND(">> ",RateName)-1)),
rateNameRng,
RateNamesRange,
retRng,
ReturnRange,
a,
XLOOKUP(IFERROR(TRIM(LEFT(tfl,FIND(", ",tfl)-1)),tfl),tnrng,XLOOKUP(rateType,rateNameRng,retRng)),
b,
IFERROR(XLOOKUP(TRIM(SUBSTITUTE(MID(SUBSTITUTE(","&tfl&REPT(" ",6),",",REPT(",",255)),2*255,255),",","")),tnrng,XLOOKUP(rateType,rateNameRng,retRng)),0),
c,
IFERROR(XLOOKUP(TRIM(MID(tfl,FIND("#",SUBSTITUTE(tfl,",","#",2))+1,255)),tnrng,XLOOKUP(rateType,rateNameRng,retRng)),0),
a+b+c))
Prefer no VBA if possible.
Using Win10, Excel 365 Monthly Enterprise channel.

Use SUMIFS with TEXTSPLIT:
=LAMBDA(TaskFormula,TaskNamesRange,ReturnRange,SUM(SUMIFS(ReturnRange,TaskNamesRange,TEXTSPLIT(TaskFormula,", "))))
TEXTSPLIT may not currently be available to all. In that case use FILTERXML in its place:
=LAMBDA(TaskFormula,TaskNamesRange,ReturnRange,SUM(SUMIFS(ReturnRange,TaskNamesRange,FILTERXML("<t><s>"&SUBSTITUTE(TaskFormula,",","</s><s>")&"</s></t>","//s"))))
Or this one that does not split the formula but looks to see if the item is included in the formula:
=LAMBDA(TaskFormula,TaskNamesRange,ReturnRange,SUM(BYCOL(SEQUENCE(,COLUMNS(TaskNamesRange),1,1),LAMBDA(a,IF(ISNUMBER(SEARCH(INDEX(TaskNamesRange,,a),TaskFormula)),INDEX(ReturnRange,,a),0)))))

Related

How to extract multiple 5-minute averages from a data frame based on specified start time?

I have second-by-second data for channels A, B, and C as shown below (this just shows the first 6 rows):
date A B C
1 2020-03-06 09:55:42 224.3763 222.3763 226.3763
2 2020-03-06 09:55:43 224.2221 222.2221 226.2221
3 2020-03-06 09:55:44 224.2239 222.2239 226.2239
4 2020-03-06 09:55:45 224.2044 222.2044 226.2044
5 2020-03-06 09:55:46 224.2397 222.2397 226.2397
6 2020-03-06 09:55:47 224.3690 222.3690 226.3690
I would like to be able to extract multiple 5-minute averages for columns A, B and C based off time. Is there a way to do this where I would only need to type in the starting time period, rather than having to type the start AND end times for each time period I want to extract? Essentially, I want to be able to type the start time and have my code calculate and extract the average for the successive 5 minutes.
I was previously using the 'time.average' function from the 'openair' package to obtain 1-minute averages for the entire data set. I then created a vector with the start times and then used the 'subset' function' to extract the 1 minute averages I was interested in.
library(openair)
df.avg <- timeAverage(df, avg.time = "min", statistic = "mean")
cond.1.time <- c(
'2020-03-06 10:09:00',
'2020-03-06 10:13:00',
'2020-03-06 10:18:00',
) #enter start times
library(dplyr)
df.cond.1.avg <- subset(df.avg,
date %in% cond.1.time) #filter data based off vector
df.cond.1.avg <- as.data.frame(df.cond.1.avg) #tibble to df
However, this approach will not work for 5-minute averages since not all of the time frames I am interested in begin in 5 minute increments of each other. Also, my previous approach forced me to only use 1 minute averages that start at the top of the minute.
I need to be able to extract 5-minute averages scattered randomly throughout the day. These are not rolling averages. I will need to extract approximately thirty 5-minute averages per day so being able to only type in the start date would be key.
Thank you!
Using the dplyr and tidyr libraries, the interval to be averaged can be selected by filtering the dates and averaged.
It doesn't seem to be efficient but it can help you.
library(dplyr)
library(tidyr)
data <- data.frame(date = seq(as.POSIXct("2020-02-01 01:01:01"),
as.POSIXct("2020-02-01 20:01:10"),
by = "sec"),
A = rnorm(68410),
B = rnorm(68410),
C = rnorm(68410))
meanMinutes <- function(data, start, interval){
# Interval in minutes
start <- as.POSIXct(start)
end <- start + 60*interval
filterData <- dplyr::filter(data, date <= end, date >= start)
date_start <- filterData$date[1]
meanData <- filterData %>%
tidyr::gather(key = "param", value = "value", A:C) %>%
dplyr::group_by(param) %>%
dplyr::summarise(value = mean(value, na.rm = T)) %>%
tidyr::spread(key = "param", value = "value")
return(cbind(date_start, meanData))
}
For one date
meanMinutes(data, "2020-02-01 07:03:11", 5)
Result:
date_start A B C
1 2020-02-01 07:03:11 0.004083064 -0.06067075 -0.1304691
For multiple dates:
dates <- c("2020-02-01 02:53:41", "2020-02-01 05:23:14",
"2020-02-01 07:03:11", "2020-02-01 19:10:45")
do.call(rbind, lapply(dates, function(x) meanMinutes(data, x, 5)))
Result:
date_start A B C
1 2020-02-01 02:53:41 -0.001929374 -0.03807152 0.06072332
2 2020-02-01 05:23:14 0.009494321 -0.05911055 -0.02698245
3 2020-02-01 07:03:11 0.004083064 -0.06067075 -0.13046909
4 2020-02-01 19:10:45 -0.123574816 -0.02373881 0.05997007

EXCEL lookup an a row / array based on a cell value

I would like to look up a row (an array) based on the DATE value, such that an array of price value (instead of a single return if using VLOOKUP) is returned for a given DATE value. Below is the data
Column A Column B Column C Column D
Row1 DATE Product A Product B Product C
Row2 1/1/2017 1 5 7
Row3 7/1/2017 3 6 5
Row4 13/1/2017 2 8 3
Thank you in advance
So one way to do this would be to make the lookup row a relative value. So assuming that your sample data is on Sheet1 and the list of dates you are looking up against is on Sheet2 Col A with a header. I would first make the lookup range a named range so that Sheet1 Col A thru Col D is named something like "Data". Then in B2 place the below formula and copy it to over to Col D and then all the way down the list of dates.
=vlookup($A2,Data,Column(B1),False)
The $ for A2 allows it to always look at column A even when copying over the formula. The Column(B1) returns a value of 2 but when you copy that formula to the left it will change to Column(c1), Column(d1)... thus changing what column of data you want returned.

Sum array of data within date range and other = text

I have a dataset with two tabs, one with monthly goal(target) and another tab with sales and order data. I'm trying to summarize sales data from the other tab into the target tab using several parameters with an Index(Match and SumIfs:
My Attempt:
=SUMIFS(INDEX(OrderBreakdown!$A$2:$T$8048,,MATCH(C2,OrderBreakdown!$G$2:$G$8048)),OrderBreakdown!$I$2:$I$8048,">="&A2,OrderBreakdown!$I$2:$I$8048,"<="&B2)
Order Breakdown is the other sheet, column D in OrderBreakdown sheet is what I want to sum if OrderBreakdown_Category(Col G) = Col C and if OrderBreakdown_Order Date(Col I) >= Start Date(Col A) and if OrderBreakdown_Order Date(Col I) <= End Date(Col A)
My answer should be much more in line with Col D but instead I'm getting $MM
Here's a sample of the dataset I'm pulling from:
dataset I'm pulling from
Ok, I am not sure why your range to sum is from A through T - that is probably where you went wrong. Also, I did not find the index method necessary. This should work for you
=SUMIFS(OrderBreakdown!$D$2:$D$8048,OrderBreakdown!$I$2:$I$8048, ">=" & A2,OrderBreakdown!$I$2:$I$8048, "<=" & B2, OrderBreakdown!$G$2:$G$8048, "<=" & C2)
Here is my sample data Starting on first sheet row 2
1/1/2011 1/30/2011 Office Supplies
Then the orderBreakdown tab starts on column C
Discount Sales Profit Quantity Category sub-category OrderDate
0.5 $45.00 ($26.00) 3 Office Supplies Paper 1/1/11 Eugene Mo Stockholm Sweden North Home Offic 1/5/11 Second Cla: Stockholm 2011-(11 0.1-2011 2011 1/1/2011
0 $854.00 $290.00 7 Furniture BookCases 1/2/2011
0 $854.00 $290.00 7 Furniture BookCases 12/32/2010

Array formula using multiplication and division across 3 columns

I have Inventory data that is in the following format:
Column D | Column E | Column F
Pack Qty | Pack Price | Total Qty
This is followed by multiple rows with various numerical values, with the odd blank row.
To calculate the stock value of any particular product/line, I use =F2/D2*E2.
To calculate the total value of stock I tried {=Sum(F:F/D:D*E:E)} but it returns a #Div/0! error.
As mentioned, some rows are blank. Some items have 0 price, others have 0 stock on hand.
I would like to avoid having to total each line in a new column then total that column.
Try this:
{=SUM(IFERROR(F:F/D:D*E:E,0))}
You can simply wrap your division inside IFERROR() and return 0.
{=SUM(IFERROR(F:F/D:D,0)*E:E)}

MATLAB receipt print random values issue

I have a project where I must make the following;
You have a small business and you sell 6 different products. Choose your products
and their prices within the range of 20p to £25.00 (these could be completely fictitious). Your
shop has 4 employees, one of whom will be at the till at the time of purchase.
Your task is to write MATLAB code to prepare a receipt for a fictitious transaction as explained
below.
There is a customer at the till. They want to purchase 3 random products with specific
quantities for each. For example, the customer wants 2 cappuccinos, 1 croissant and 6 raspberry
muffins.
(1) Select randomly 3 products from your list. For each product choose a random quantity
between 1 and 9.
(2) Calculate the total cost.
(3) Choose randomly the staff member to complete the transaction.
(4) Suppose that the price includes 20% VAT. Calculate the amount of VAT included in the price.
(6) Prepare the receipt as text in the MATLAB command window. Use the current date and time
(check datestr(now,0)).
Your code should output the receipt in the format shown in the picture. There should be
60 symbols across. Choose our own shop name.
My code so far is the following:
clear all
clc
close all
items = {'apples ','carrots ','tomatoes','lemons ','potatoes','kiwis '};% products
price = {3.10, 1.70, 4.00, 1.65, 9.32, 5.28};% item prices. I set spaces for each entry in order to maintain the border format.
employee = {'James','Karina','George','Stacey'};%the employees array
disp(sprintf('+-----------------------------------------------+'));
disp(sprintf('|\t%s \t\t\tAlex''s Shop |\n|\t\t\t\t\t\t\t\t\t\t\t\t|', datestr(now,0)));
totalPrice = 0;
for i = 1:3
randItems = items {ceil(rand*6)};
randprice = price {ceil(rand*6)};
randQuantity = ceil(rand*9);% random quantity from 1 to 9 pieces
randEmployee = employee{ceil(rand*4)};
itemTotal = randprice * randQuantity;%total price of individual item
totalPrice = totalPrice + itemTotal;
disp(sprintf('|\t%s\t (%d) x %.2f = £ %.2f \t\t\t|', randItems, randQuantity, randprice, itemTotal))
end
disp(sprintf('|\t\t\t\t-----------------------------\t|'));
disp(sprintf('|\t\t\t\t\t\t\t\t\t\t\t\t|\n|\t Total to pay \t £ %.2f\t\t\t\t|',totalPrice));
disp(sprintf('|\t VAT \t\t\t\t £ %.2f\t\t\t\t| \n|\t\t\t\t\t\t\t\t\t\t\t\t|', totalPrice*0.2));
disp(sprintf('|\tThank you! You have been served by %s\t|\t', randEmployee));
disp(sprintf('+-----------------------------------------------+'));
My issue of course is the following. Upon choosing a random item from the items list, I then choose a random price to assign as well. I don't want this though. I would like to find a way to assign a preset price to each item to be printed automatically when generating a random item to be added to the basket. I hope this explanation is sufficient for you, if you have any questions feel free to ask. Thank you in advance.
When you write
randItems = items {ceil(rand*6)};
randprice = price {ceil(rand*6)};
you calculate a random index into the array items, and then you calculate a random index into the array price. If you instead assign the index you calculate via ceil(rand*6) to a separate variable, called e.g. index, you can re-use it to pick, say, item #3 from both items and price. Thus, the ith item will always show up with the ith price.

Resources