icCube - InterpolateRGBColors based on min & max values? - colors

I understand that InterpolateRGBColors function is returning a color by position of value between 0 and 1... So its seems to be doable only with percentages, not numbers...
Is there a way to have the same functionality, but based on the min and max values returned in a set ?
What I want is to attribute colors to my measure but in a range of min([Measures].[NbSejours]) to max([Measures].[NbSejours]) ( not 0 to 1)...
WITH
MEMBER [Measures].[color] AS
InterpolateRGBColors(
[Measures].[NbSejours]
,rgb(176,224,230)
,rgb(135,206,235)
,rgb(0,191,255)
,rgb(100,149,237)
,rgb(0,0,255)
,rgb(0,0,139)
,rgb(25,25,112)
), BACK_COLOR=currentCellValue()
SELECT
{
{[Measures].[NbSejours]}
,[Measures].[color]
} ON COLUMNS
,{
NonEmpty
(
[Etablissement].[Etablissement].[Etablissement].ALLMEMBERS
,[Measures].[NbSejours]
)
} ON ROWS
FROM
(
SELECT
{{[Periode].[Periode].[All-M].&[2013]}} ON 0
FROM [Cube]
)
CELL PROPERTIES
STYLE
,CLASSNAME
,VALUE
,FORMATTED_VALUE;
Is there a way to do that ?

InterpolateRGBColors expect a numerical between 0 and 1 for interpolation. So we need to scale our measure to ensure we get the right colors.
There is an example in our live demo , here.
What we need is to scale [Measures].[NbSejours] between 0,1. There are two no documented function in icCube DistributionFlat & DistributionRank.
A non efficient version
WITH
SET [AxisX] AS NonEmpty([Etablissement].Etablissement].Etablissement].ALLMEMBERS,[Measures].[NbSejours])
FUNCTION distr(x_) as DistributionFlat( [AxisX], [Measures].[NbSejours], x_ )
MEMBER [Measures].[color] AS
InterpolateRGBColors(
distr([Measures].[NbSejours])
,rgb(176,224,230)
,rgb(135,206,235)
,rgb(0,191,255)
,rgb(100,149,237)
,rgb(0,0,255)
,rgb(0,0,139)
,rgb(25,25,112)
), BACK_COLOR=currentCellValue()
....
Once I got a bit of time I'll write a version using Vectors (here and here) that is more performant as in the example above we calculate every time the values for the set.
Hope it helps

I don'r know icCube so the following might not work, even though I have used standard functions. As #George commented you can use the standard RANK function to find each members relative position.
You will need to feed that value into the definition of [Measures].[color]...
WITH
SET [estMembersOrdered] AS
ORDER(
[Etablissement].[Etablissement].[Etablissement].ALLMEMBERS
,[Measures].[NbSejours]
,BDESC
)
MEMBER [Measures].[rnkEtablissement] AS
RANK(
[Etablissement].[Etablissement].CURRENTMEMBER
, [estMembersOrdered]
)
MEMBER [Measures].[color] AS
InterpolateRGBColors(
[Measures].[NbSejours]
,rgb(176,224,230)
,rgb(135,206,235)
,rgb(0,191,255)
,rgb(100,149,237)
,rgb(0,0,255)
,rgb(0,0,139)
,rgb(25,25,112)
), BACK_COLOR=currentCellValue()
SELECT
{
{[Measures].[NbSejours]}
,[Measures].[color]
,[Measures].[rnkEtablissement]
} ON COLUMNS
,{
NonEmpty
(
[Etablissement].[Etablissement].[Etablissement].ALLMEMBERS
,[Measures].[NbSejours]
)
} ON ROWS
FROM
(
SELECT
{{[Periode].[Periode].[All-M].&[2013]}} ON 0
FROM [Cube]
)
CELL PROPERTIES
STYLE
,CLASSNAME
,VALUE
,FORMATTED_VALUE;

Related

ArangoDB AQL: Find Gaps In Sequential Data

I've been given data to build an application that has sequential data in the form of part numbers of products: "000000", "000001", "000002", "000010", "000011" .... The previous application was an old MS Access database that didn't have any gap filling features in the part number generator, hence the gap between "000002" and "000010" (Yes, they are also strings, but I can work with that...).
We could continue to increment based on the last value and ignore the gaps, however, in an attempt to use all numbers available to us with our naming scheme, we'd like to be able to fill the gaps. Our naming scheme describes the "product family" with the first two digits such that: [00]0000 would be a different family from [02]0000.
I can find the starting and ending values using something like:
let query = `
LET first = (
MIN(
FOR part in part_search
SEARCH STARTS_WITH(part.PartNumber, #family)
RETURN part.PartNumber
)
)
LET last = (
MAX(
FOR part in part_search
SEARCH STARTS_WITH(part.PartNumber, #family)
RETURN part.PartNumber
)
)
RETURN { first, last }
`
The above example returns: {first: "000000", last: "000915"}
Using ArangoDB and AQL, how could I go about finding these gaps? I've found some SQL examples but I feel the features of AQL are a bit more limiting.
Thanks in advance!
To start with, I think your best bet for getting min/max values is using aggregates:
FOR part in part_search
SEARCH STARTS_WITH(part.PartNumber, #family)
COLLECT x = 1
AGGREGATE first = MIN(part.PartNumber), last = MAX(part.PartNumber)
RETURN {
first: first,
last: last
}
But that won't really help when trying to find gaps. And you're right - SQL has several logical constructs that could help (like using variables and cursor iteration), but even that would be a pattern I would discourage.
The better path might be to do a "brute force" approach - compare a table containing your existing numbers with a table of all numbers, using a native method like JOIN to find the difference. Here's how you might do that in AQL:
LET allNumbers = 0..9999
LET existingParts = (
FOR part in part_search
SEARCH STARTS_WITH(part.PartNumber, #family)
LET childId = RIGHT(part.PartNumber, 4)
RETURN TO_NUMBER(childId)
)
RETURN MINUS(allNumbers, existingParts)
The x..y construct creates a sequence (an array of numbers), which we use as the full set of possible numbers. Then, we want to return only the "non-family" part of the ID (I'm calling it "child"), which needs to be numeric to compare with the previous set. Then, we use MINUS to remove elements of existingParts from the allNumbers list.
One thing to note, that query would return only the "child" portion of the part number, so you would have to join it back to the family number later. Alternatively, you could also skip string-splitting, and get "fancy" with your list creation:
LET allNumbers = TO_NUMBER(CONCAT(#family, '0000'))..TO_NUMBER(CONCAT(#family, '9999'))
LET existingParts = (
FOR part in part_search
SEARCH STARTS_WITH(part.PartNumber, #family)
RETURN TO_NUMBER(part.PartNumber)
)
RETURN MINUS(allNumbers, existingParts)

Split string mixed input into range of single values

I have an input which allows multiple IDs.
They can be entered like this:
[ 1000, 1001, 1050-1060, 1100 ]
Out of this input string I want to get all the single IDs.
I already found this to split after each ,, so the part with 1000, 1001 already works.
data : itab TYPE TABLE OF string,
SPLIT l_bukrs_string AT ';' INTO TABLE itab.
My problem is the self-built range. Any idea how I could combine this with the case above to split 1050-1060 into single values?
I want to get 1050 | 1051 | 1052 | ... | 1060 out of it.
Appreciate every hint :) Thank you so much!
The easiest solution would be to use a real range/select-option for user (?) input instead. Then you would use that range to select every value from the database table.
If you cannot use a real range/select-option, then you could convert the string to one as shown below.
DATA: bukrs_string TYPE string,
split_bukrs TYPE TABLE OF string,
bukrs TYPE bukrs,
bukrs_between TYPE TABLE OF bukrs,
bukrs_range TYPE RANGE OF bukrs,
bukrs_rline LIKE LINE OF bukrs_range,
bukrs_table TYPE TABLE OF bukrs.
FIELD-SYMBOLS: <string> TYPE string,
<bukrs> TYPE bukrs,
<bukrs_from> TYPE bukrs,
<bukrs_to> TYPE bukrs.
bukrs_string = '1000, 1001, 1050-1060, 1100'.
CONDENSE bukrs_string NO-GAPS.
SPLIT bukrs_string AT ',' INTO TABLE split_bukrs.
LOOP AT split_bukrs ASSIGNING <string>.
bukrs_rline-sign = 'I'.
IF <string> CA '-'.
SPLIT <string> AT '-' INTO TABLE bukrs_between.
bukrs_rline-option = 'BT'.
READ TABLE bukrs_between INDEX 1 ASSIGNING <bukrs_from>.
bukrs_rline-low = <bukrs_from>.
READ TABLE bukrs_between INDEX 2 ASSIGNING <bukrs_to>.
bukrs_rline-high = <bukrs_to>.
ELSE.
bukrs_rline-option = 'EQ'.
bukrs = <string>.
bukrs_rline-low = bukrs.
ENDIF.
APPEND bukrs_rline TO bukrs_range.
CLEAR bukrs_rline.
ENDLOOP.
SELECT bukrs
FROM t001
INTO TABLE bukrs_table
WHERE bukrs IN bukrs_range.
Before you split the string, you would condense it, to remove all spaces. Then you would loop over the resulting parts and check if it contains any '-'. If that is the case, you split it again and create a BETWEEN entry in your range (consider if you may want an additional check to see if the latter number is actually higher). If there is no '-', you just create an EQUAL entry.
After you have your real range, you use it to select from the database. This is because not every bukrs in that range has to exist. You may only have 1000, 1050, 1055 and 1060, for example.
Edit: The reason there is no command, function module or class to convert a range to individual values is because what needs to be done changes heavily depending on WHAT data the range is for and if/how much values need to be verified.
If you have an integer range, then all you need to do is take the from-value and add 1 to it until you reach the to-value. What about a range of binary floating point numbers? What about a range of colours? What about your range of company codes, where not all of them necessarily exist? That's why the conversion has to be done manually.
Provided you were given a string with a list of mixed values, both single and interval BUKRS values divided by dash, and this list is separated by comma+space, then
DATA: input TYPE string VALUE '1000, 1001, 1050-1060, 1100, 1300-1340',
itab TYPE TABLE OF char10,
r_bukrs TYPE RANGE OF bukrs.
SPLIT input AT `, ` INTO TABLE itab.
r_bukrs = VALUE #( FOR GROUPS bukrs OF <bukrs> IN itab WHERE ( table_line+4(1) NE '-' ) GROUP BY <bukrs> WITHOUT MEMBERS ( sign = 'I' option = 'EQ' low = bukrs ) ).
DATA(ranges) = VALUE ddtest_ttyp_char( FOR GROUPS bukrs OF <bukrs> IN itab WHERE ( table_line+4(1) EQ '-' ) GROUP BY <bukrs> WITHOUT MEMBERS ( bukrs ) ).
LOOP AT ranges ASSIGNING FIELD-SYMBOL(<range>).
r_bukrs = VALUE #( BASE r_bukrs FOR j = CONV i( <range>(4) ) UNTIL j = CONV i( <range>+5(4) ) + 1 ( sign = 'I' option = 'EQ' low = j ) ).
ENDLOOP.
The first table expression (7th line) fills r_bukrs with unique values from initial table string.
The second table expression (8th line) fills ranges table with dash ranges found in initial table string, 1050-1060 and 1300-1340 in our case.
In the loop through ranges table the <range>(4) is the left extrema of interval, and <range>+5(4) is the right extrema, e.g. 1300 and 1340 correspondingly for last value interval.

Query a range in an Excel table linked to PowerApps Text Search box

First time asking a question here. As well as being pretty new to PowerApps as well.
I am trying to use two text input boxes for the user to define the min & max of their number range. basically i want the code to return all results that fall in the user defined range.
User inputs are:
SearchText.Text
MinSearch.Text and
MaxSearch.Text
PDFData is the table, and
RMANumber is the column that i want the Min & Max to search and return all within the user defined range. as of now, all i can get this to return are exact results, which just won't work for my situation. In my way of thinking, i want to add WHERE after the RAWidth and give greater or lesser arguments, but this isn't working for me. My full code is below, and any help is appreciated.
If(SearchText.Text="" && MinSearch.Text="" && MaxSearch.Text="", PDFData, Filter(PDFData,SearchText.Text in PDFAuthor|| SearchText.Text in PDFName|| SearchText.Text in RMANumber|| MinSearch.Text in RAWidth))
You can use the following expression for your query:
Filter(
PDFData,
SearchText.Text in PDFAuthor || SearchText.Text in PDFName,
Coalesce(Value(MinSearch.Text), -1) <= RAWidth,
Coalesce(Value(MaxSearch.Text), 1000000000) >= RAWidth)
If the SearchText is empty, then the conditions SearchText.Text in PDFAuthor and SearchText.Text in PDFName will both be true anyway, so there's no need for the If at that point.
For the other conditions, we can use the functions Value / Coalesce to convert the text input to a number; if the user didn't enter anything (or entered an invalid number), then the function Value will return a blank value, and the Coalesce function will use the next value. I'm using here -1 for the minumum value and 1000000000 for the maximum - if the possible range of values in your RAWidth column is between those numbers then you're fine.

IF function for specific text strings

I am trying to write a formula that takes a word and process it through a IF function in excel, The Values are list in the formula. My issue right now is the fact that I have Large, X-Large and 1X-Large text. The X-Large and 1X-Large are unique strings and need the IF function to be able to differentiate the two.
Here is what i have so far.
=if(or(isnumber(search("Small",af2)),ISNUMBER(SEARCH("Medium",AF2)),ISNUMBER(SEARCH("Large",AF2)),,ISNUMBER(SEARCH("X-Large",AF2))),"Small",or(isnumber(search("1X-Large",af2)),isnumber(search("2X-Large",af2)),isnumber(search("3X-Large",af2)),isnumber(search("4X-Large",af2))),"1X-Large")
I cant understand why it's showing an error and only displays small when it works.
All help is appreciated
Your current formula shouldn't work, it should be giving you an error about having too many arguments. A breakdown of your function:
=if(
or(isnumber(search("Small",af2)),ISNUMBER(SEARCH("Medium",AF2)),ISNUMBER(SEARCH("Large",AF2)),,ISNUMBER(SEARCH("X-Large",AF2))),
"Small",
or(isnumber(search("1X-Large",af2)),isnumber(search("2X-Large",af2)),isnumber(search("3X-Large",af2)),isnumber(search("4X-Large",af2))),
"1X-Large"
)
You cannot use 4 parameters in an IF. You need to have a maximum of 3. Maybe what you meant was:
=if(
or(isnumber(search("Small",af2)),ISNUMBER(SEARCH("Medium",AF2)),ISNUMBER(SEARCH("Large",AF2)),,ISNUMBER(SEARCH("X-Large",AF2))),
"Small",
IF(
or(isnumber(search("1X-Large",af2)),isnumber(search("2X-Large",af2)),isnumber(search("3X-Large",af2)),isnumber(search("4X-Large",af2))),
"1X-Large"
)
)
But that doesn't solve your issue about the X-Large part. To cater for that, you can check whether the X-Large series exist first, then the others.
=IF(
OR(ISNUMBER(SEARCH("1X-Large",AF2)),ISNUMBER(SEARCH("2X-Large",AF2)),ISNUMBER(SEARCH("3X-Large",AF2)),ISNUMBER(SEARCH("4X-Large",AF2))),
"1X-Large",
IF(
OR(ISNUMBER(SEARCH("Small",AF2)),ISNUMBER(SEARCH("Medium",AF2)),ISNUMBER(SEARCH("Large",AF2)),ISNUMBER(SEARCH("X-Large",AF2))),
"Small"
)
)
Although you can make it shorter with this:
=IF(
OR(ISNUMBER(SEARCH({"1X-Large","2X-Large","3X-Large","4X-Large"},AF2))),
"1X-Large",
IF(
OR(ISNUMBER(SEARCH({"Small","Medium","Large","X-Large"},AF2))),
"Small"
)
)
In one line...
=IF(OR(ISNUMBER(SEARCH({"1X-Large","2X-Large","3X-Large","4X-Large"},AF2))),"1X-Large",IF(OR(ISNUMBER(SEARCH({"Small","Medium","Large","X-Large"},AF2))),"Small"))

Converting Excel functions into R

I have two excel functions that I am trying to convert into R:
numberShares
=IF(AND(N213="BOH",N212="BOH")=TRUE,P212,IF(AND(N213="BOH",N212="Sell")=TRUE,ROUNDDOWN(Q212/C213,0),0))
marketValue
=IF(AND(N212="BOH",N213="BOH")=TRUE,C213*P212,IF(AND(N212="Sell",N213="Sell")=TRUE,Q212,IF(AND(N212="BOH",N213="Sell")=TRUE,P212*C213,IF(AND(N212="Sell",N213="BOH")=TRUE,Q212))))
The cells that are referenced include:
c = closing price of a stock
n = position values of either "buy or hold" or "sell"
p = number of Shares
q = market value, assuming $10,000 initial equity (number of shares*closing price)
and the tops of the two output columns that i am trying to recreate look like this:
output
So far, in R I have constructed a dataframe with the necessary four columns:
data.frame
I just don't know how to write the functions that will populate the number of shares and market value columns. For loops? ifelse?
Again, thank you!!
Covert the AND()'s to infix "&"; the "=" to "=="; and the IF's to ifelse() and you are halfway there. The problem will be in converting your cell references to array or matrix references, and for that task we would have needed a better description of the data layout:
numberShares <-
ifelse( N213=="BOH" & N212=="BOH",
#Perhaps PosVal[213] == "BOH" & PosVal[212] == "BOH"
# ... and very possibly the 213 should be 213:240 and the 212 should be 212:239
P212,
ifelse( N213=="BOH" & N212=="Sell" ,
round(Q212/C213, digits=0),
0))
(You seem to be returning incommensurate values which seems preeety questionable.) Assuming this is correct code despite my misgivings the next translation involves apply the same substitutions in this structure (although you seem to be missing an else-consequent in the last IF function:
marketValue <-
IF( AND(N212="BOH", N213="BOH")=TRUE,
C213*P212,
IF(AND(N212="Sell",N213="Sell")=TRUE,
Q212,
IF( AND(N212="BOH",N213="Sell")=TRUE,
P212*C213,
IF(AND(N212="Sell",N213="BOH")=TRUE,
Q212))))
(Your testing for AND( .,.)=TRUE is I believe unnecessary in Excel and certainly unnecessary in R.)

Resources