I want to make a tooltip that, when an item in a chart is hovered over, shows the top 3 names in a column that's ranked by the number of times they appear.
I found two pieces of code that each do half of the trick but have tried and failed to combine them.
Code to find the most commonly occurring value:
TopIssue =
FIRSTNONBLANK (
TOPN (
1,
VALUES ( FlagReport[Cat 3] ),
RANKX( ALL( FlagReport[Cat 3] ), [IssueCount],,ASC)
),
1)
Where IssueCount = COUNT(FlagReport[Ref No])
This works fine, but when I change the 1 -> 2 -> 3 it doesn't correlate correctly with the ranking as when I change it to 2 it doesn't show the correct value
Code to show the first 3 string values that occur:
List of Cat 3 values =
VAR __DISTINCT_VALUES_COUNT = DISTINCTCOUNT('FlagReport'[Cat 3])
VAR __MAX_VALUES_TO_SHOW = 3
RETURN
IF(
__DISTINCT_VALUES_COUNT > __MAX_VALUES_TO_SHOW,
CONCATENATE(
CONCATENATEX(
TOPN(
__MAX_VALUES_TO_SHOW,
VALUES('FlagReport'[Cat 3]),
'FlagReport'[Cat 3],
ASC
),
'FlagReport'[Cat 3],
", ",
'FlagReport'[Cat 3],
ASC
),
", etc."
),
CONCATENATEX(
VALUES('FlagReport'[Cat 3]),
'FlagReport'[Cat 3],
", ",
'FlagReport'[Cat 3],
ASC
)
)
This code shows me the first 3 string values but doesn't let me rank them.
I have been trying and failing with this for far too long considering it sounds like a theoretically simple thing to do.
I think you're making it a bit more complex than it needs to be. The first code gets you most of the way there. You just need to wrap it in a concatenate function and make sure you have the ordering set correctly.
Top3 = CONCATENATEX(
TOPN(3,
VALUES(FlagReport[Cat 3]),
RANKX(ALL(FlagReport[Cat 3]), [IssueCount], ,ASC)),
FlagReport[Cat 3], ", ", [IssueCount], DESC)
The TOPN function finds the top 3 ranked items. Then we concatenate the Cat 3 column using [IssueCount] as the order by expression.
Related
I would like this multiple-criteria query not to show empty columns.
=QUERY({H3:M11}, "select * WHERE
"&TEXTJOIN(" and ", 1,
IF(C3<>"", "Col2 = "&C3&"", ),
IF(B3<>"", "Col3 = '"&B3&"'", )), 1)
Besides, I would also like to know if it's possible to filter it outside a query formula. Currently, I have this formula made by #player0 which is excluding columns with values greater than 0, but I didn't manage to make it work for text.
=FILTER(FILTER(H3:M11, LEN(TRIM(QUERY(IFERROR(1/(1/H4:M11)),,9^9)))>0), {9;
LEN(TRIM(FLATTEN(QUERY(TRANSPOSE(IFERROR(1/(1/H4:M11))),,9^9))))}>0)
Link to the question where this filter formula was found.
Here's the sheet.
Thanks a lot.
try:
=ARRAYFORMULA(QUERY({H3:K11,
FILTER(L3:M11, TRIM(QUERY(L4:M11,,9^9))<>"")},
"where "&TEXTJOIN(" and ", 1,
IF(C3<>"", "Col2 = "&C3&"", ),
IF(B3<>"", "Col3 = '"&B3&"'", )), 1))
I have a stream of data that looks like this:
impressionId | id | name | eventType | timestamp
I need to filter (ignore) event of type "click" that don't have a matching 'impressionId' of type 'impression' (so basically ignore clicks event that don't have an impression)
and then count how many impressions in total I have and how many clicks I have (for an id/name pair) for a particular time window.
This is how I approached the solution:
[...]
Table eventsTable = tEnv.fromDataStream(eventStreamWithTimeStamp, "impressionId, id, name, eventType, eventTime.rowtime");
tEnv.registerTable("Events", eventsTable);
Table clicksTable = eventsTable
.where("eventType = 'click'")
.window(Slide.over("24.hour").every("1.minute").on("eventTime").as("minuteWindow"))
.groupBy("impressionId, id, name, eventType, minuteWindow")
.select("impressionId as clickImpressionId, eventType as clickEventType, concat(concat(id,'_'), name) as concatClickId, id as clickId, name as clickName, minuteWindow.rowtime as clickMinute");
Table impressionsTable = eventsTable
.where("eventType = 'impression'")
.window(Slide.over("24.hour").every("1.minute").on("eventTime").as("minuteWindow"))
.groupBy("impressionId, id, name, eventType, minuteWindow")
.select("impressionId as impressionImpressionId, eventType as impressionEventType, concat(concat(id,'_'), name) as concatImpId, id as impId, name as impName, minuteWindow.rowtime as impMinute");
Table filteredClickCount = clicksTable
.join(impressionsTable, "clickImpressionId = impressionImpressionId && concatClickId = concatImpId && clickMinute = impMinute")
.window(Slide.over("24.hour").every("1.minute").on("clickMinute").as("minuteWindow"))
.groupBy("concatClickId, clickMinute")
.select("concatClickId, concatClickId.count as clickCount, clickMinute as eventTime");
DataStream<Test3> result = tEnv.toAppendStream(filteredClickCount, Test3.class);
result.print();
What I'm trying to do is simply create two tables, one with clicks and one with impressions, 'inner' join clicks to impressions and the one that are joined means they are the clicks that have a matching impression.
Now this doesn't work and I don't know why!?
the count produced by the last joint table are not correct. It works for the first minute but after that the counts are off by almost double.
I have then tried to modify the last table like this:
Table clickWithMatchingImpression2 = clicksTable
.join(impressionsTable, "clickImpressionId = impressionImpressionId && concatClickId = concatImpId && clickMinute = impMinute")
.groupBy("concatClickId, clickMinute")
.select("concatClickId, concatClickId.count as clickCount, clickMinute as eventTime");
DataStream<Tuple3<Boolean, Tuple3>> result2 = tEnv.toRetractStream(clickWithMatchingImpression2, Test3.class);
result2.print();
And.... this works !? However I don't know why and I don't know what to do with this DataStream<Tuple3<Boolean, Test3>> format... Flink refuse to use toAppendStream when the table don't have a window.
I would like a simply structure with only the final numbers.
1 ) Is my approach correct? Is there an easier way of filtering click that don't have impressions ?
2 ) Why does the counts are not correct in my solution ?
I am not entirely sure if I understood your use case correctly, an example with some data points would definitely help here.
Let me explain what your code is doing. First the two tables calculate how many clicks/impressions there were in the last 24 hours.
For an input
new Event("1", "1", "ABC", "...", 1),
new Event("1", "2", "ABC", "...", 2),
new Event("1", "3", "ABC", "...", 3),
new Event("1", "4", "ABC", "...", 4)
You will get windows (array<eventId>, window_start, window_end, rowtime):
[1], 1969-12-31-01T00:01:00.000, 1970-01-01T00:01:00.000, 1970-01-01T00:00:59.999
[1, 2], 1969-12-31-01T00:02:00.000, 1970-01-01T00:02:00.000, 1970-01-01T00:01:59.999
[1, 2, 3], 1969-12-31-01T00:03:00.000, 1970-01-01T00:03:00.000, 1970-01-01T00:02:59.999
...
Therefore when you group both on id and name you get sth like:
1, '...', '1_ABC', 1, 'ABC', 1970-01-01T00:00:59.999
1, '...', '1_ABC', 1, 'ABC', 1970-01-01T00:01:59.999
1, '...', '1_ABC', 1, 'ABC', 1970-01-01T00:02:59.999
...
which if you group again in 24 hours windows you will count each event with the same id multiple times.
If I understand your use case correctly and you are looking for how many impressions happened in a 1 minute period around an occurrence of a click, an interval join might be what you are looking for. You could implement your case with a following query:
Table clicks = eventsTable
.where($("eventType").isEqual("click"))
.select(
$("impressionId").as("clickImpressionId"),
concat($("id"), "_", $("name")).as("concatClickId"),
$("id").as("clickId"),
$("name").as("clickName"),
$("eventTime").as("clickEventTime")
);
Table impressions = eventsTable
.where($("eventType").isEqual("impression"))
.select(
$("impressionId").as("impressionImpressionId"),
concat($("id"), "_", $("name")).as("concatImpressionId"),
$("id").as("impressionId"),
$("name").as("impressionName"),
$("eventTime").as("impressionEventTime")
);
Table table = impressions.join(
clicks,
$("clickImpressionId").isEqual($("impressionImpressionId"))
.and(
$("clickEventTime").between(
$("impressionEventTime").minus(lit(1).minutes()),
$("impressionEventTime"))
))
.select($("concatClickId"), $("impressionEventTime"));
table
.window(Slide.over("24.hour").every("1.minute").on("impressionEventTime").as("minuteWindow"))
.groupBy($("concatClickId"), $("minuteWindow"))
.select($("concatClickId"), $("concatClickId").count())
.execute()
.print();
As for why Flink sometimes cannot produce append stream, but only retract stream see. Very briefly, if an operation does not work based on a time attribute, there is not single point in time, when the result is "valid". Therefore it must emit stream of changes instead of a single appended value. The first field in the tuple tells you if the record is an insertion(true) or retraction/deletion(false).
I'm looking for the best high order function to use on this array of arrays to return a bool if there is a submit_instance_key value of 1.
I've looked into using a filter and reduce but I'm having trouble dealing with the nested arrays. I've also tried to compactMap it but it changes the shape of the array.
var someArray = [["submit_instance_key": 0, "instance_id_key": 4], ["submit_instance_key": 0, "instance_id_key": 4], ["submit_instance_key": 1, "instance_id_key": 5]]
I'm hoping to return a Bool if there is a submit_instance_key value of 1.
The best high level function to use is one that stops searching as soon as it finds a match. contains(where:) is a good function to use for this purpose. It takes a closure that gets applied to each element of the array in turn and stops as soon as it finds an element that returns true.
var someArray = [["submit_instance_key": 0, "instance_id_key": 4], ["submit_instance_key": 0, "instance_id_key": 4], ["submit_instance_key": 1, "instance_id_key": 5]]
let found = someArray.contains(where: { $0["submit_instance_key"] == 1 })
print(found)
Output:
true
Im trying to find an easy way to create matrix with self incrementing values i.e., if 3x3 array then it should look like
[[0,1,2],[3,4,5],[6,7,8]]
when I do something like below, I could get all zero's
var arr = Array(repeating: Array(repeating: 0, count: 3), count: 3)
Inorder to acheive, I need to loop through elements and reassign incremented values. Instead of that is there any fast approach I could follow without using for-loop?
A possible approach is to use map() on the range of rows and columns:
let nrows = 3 // Number of rows
let ncols = 3 // Number of columns
let matrix = (0..<nrows).map { row in (0..<ncols).map { col in ncols * row + col } }
print(matrix) // [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
The outer (0..<nrows).map maps each row number to an array (the “row”), and the inner (0..<ncols).map maps each column number to a matrix entry.
With a little bit “tuple magic” you could assign auto-incrementing values:
var entry = 0
let matrix = (0..<nrows).map { _ in (0..<ncols).map { _ in (entry, entry += 1).0 } }
but that's not what I would really recommend.
There is the a Subscripts tutorial on Swift.org showing a Matrix struct example, which I really like
I'm not sure if this is possible due to the numerical indices, but hopefully someone can point me in the right direction.
Given the table of:
t = { 13, 200, 12, 15, 23 }
how can I nest a table using the numbers?
t["200"] = {"stuff", "more stuff", "even more stuff"}
doesn't seem to work, as it'll create a position 200 and fill in the empty cells with null. I'd add a letter as a suffix/prefix, but the problem comes trying to sort the table numerically. Is this even possible, or am I stuck with a different method? Thanks!
Slight edit due to a realisation:
t["200"] = {"stuff", "more stuff", "even more stuff"}
actually creates a key of "200", whereas:
t[200] = {"stuff", "more stuff", "even more stuff"}
creates the index 200 with everything else null.
First, DeadMG is correct; you used a string rather than a numerical index. However, even if you did use a number index, it wouldn't help.
If you do this:
someTable = {"value1", "value2", {"value3a", "value3b"}};
someTable[50] = {"value50a", "value50b"};
The length of the table, #someTable, will still be 3. Why? Because Lua defines arrays in a table based on contiguous elements. Remember: you can access any element of any table; they are all conceptually filled with nil until you give them an actual value.
Lua defines length for a table as the number of values in a table if you start counting from numerical index 1 until you reach the first nil value. Since someTable[4] is nil, the length is 3.
If you want to insert a new element at the end of an array table, then you can do this:
someTable[#someTable + 1] = "newValue";
The value can itself be a table:
someTable[#someTable + 1] = {"newValuea", "newValueb"};
If you're just asking how to access a nested table, that's simple, and it has nothing to do with the keys you use.
There is nothing special about nested tables. Tables are values, and table entries can be any value, including other tables.
If you have a table, and want to walk the array entries in it, you use this:
local aTable = {"first", "second", "third", ...}
for i, value in ipairs(aTable) do
--`value` contains the entries in the table.
end
A nested table is no different; it is simply a matter of getting the table.
local nestedTable = { "first", "second", "third", ...}
nestedTable[#nestedTable + 1] = {"newFirst", "newSecond", ...}
local aTable = nestedTable[#nestedTable];
for i, value in ipairs(aTable) do
--`value` contains the entries in the table.
end
Or you could just do ipairs(nestedTable[#nestedTable]). Note that the particular key used here (an integer value) is entirely unimportant. That key could have been a string, a floating-point number, another table, some user-data, etc. It doesn't matter.
Note also that we use ipairs because we only want to iterate over the array members of the table. The length of the array is defined above. If we wanted to loop over every member of the table, we would use pairs instead of ipairs. Of course, pairs does an unordered search, so it is not guaranteed to be in array order.
If you want to recursively find every element in a nested table, you can do this:
local function RecursiveSearch(aTable)
for key, value in pairs(aTable) do --unordered search
if(type(value) == "table") then
RecursiveSearch(value)
else
--Do something with this.
end
end
end
Note that the above can do an infinite loop, since it is possible for a table to have circular references:
local tableA = {}
local tableB = {tableA}
local tableA[1] = tableB
RecursiveSearch(tableA) --Infinite loop.
Perhaps it helps to view your assignment like this:
t = { [1] = 13, [2] = 200, [3] = 12, [4] = 15, [5] = 23 }
To change what is currently 200 (namely t[2]), you do:
t[2] = {"stuff", "more stuff", "even more stuff"}
Edit: that results in your table looking like this:
t = { [1] = 13, [2] = {"stuff", "more stuff", "even more stuff"}, [3] = 12, [4] = 15, [5] = 23 }
-- or, equivalent::
t = { 13, {"stuff", "more stuff", "even more stuff"}, 12, 15, 23 }
The trouble is your use of "". Your table t contains a bunch of numbers, and you're entering a string as the key. You want to iterate over the table and do... something that you didn't particularly well define. However, you can't add to a table whilst iterating over it, so you might have to do some funny stuff.
t = { 13, 200, 12, 15, 23 }
newt = {};
for key, value in pairs(t) {
newt[value] = { };
}
This will create a table entry in newt, where the key is a value in the table t, for all values in t.