I have the following test Data set, with 3 different levels of grouping. And I want to make a measure "Value WA" for Value, and the make a matrix like showing in the image after. Basically, it is the weighted average of "Value" based on "Market Value“ for the sub-categories contained in it. If it is on row of "Portfolio1", it is the WA of Index1 and Index2, and if it is on row of "Class1", it is the WA of "Portfolio1" and "Portfolio2" etc.
I had the code for measure "Value WA" at the bottom using netted SUMMARIZE. But somehow it doesn't work appropriately. I can't figure out why, but I guess it is on the last line of "RETURN SUMX(SubTable2, [Value2] * [WT2])"
Please help me with the correct approach. Thank you very much!
Date set
GroupLevel1
GroupLevel2
GroupLevel3
Market Value
Value
Class1
Portfolio1
Index1
30
3
Class1
Portfolio1
Index2
30
2
Class1
Portfolio1
Index8
30
4
Class1
Portfolio2
Index3
50
4
Class2
Portfolio3
Index4
50
6
Class2
Portfolio4
Index5
50
4
Class3
Portfolio5
Index6
200
5
Class3
Portfolio5
Index7
200
1
what I want it to look like:
Value WA =
IF( ISFILTERED('Table'[GroupLevel3]),
CALCULATE(SUM('Table'[Value])),
VAR SubTable1 =
ADDCOLUMNS(
SUMMARIZE('Table','Table'[GroupLevel3],'Table'[GroupLevel2],'Table'[GroupLevel1]),
"WT3", CALCULATE(SUM('Table'[Market Value])) /
CALCULATE(SUM('Table'[Market Value]),
ALL('Table'[GroupLevel3])),
"Value3", CALCULATE(SUM('Table'[Value])))
VAR Val_level2 = SUMX(SubTable1, [Value3] * [WT3])
RETURN
IF( ISFILTERED('Table'[GroupLevel2]),
Val_level2,
VAR SubTable2 = ADDCOLUMNS(
SUMMARIZE(SubTable1,[GroupLevel2]),
"WT2", CALCULATE(SUM('Table'[Market Value])) /
CALCULATE(SUM('Table'[Market Value]),
ALL('Table'[GroupLevel2])),
"Value2", Val_level2)
RETURN SUMX(SubTable2, [Value2] * [WT2])
)
)
Related
Hi I am trying to add a AVERAGE column in a matrix, but when I put my metric added the average per column, but I need a total AVERAGE and total at the end just once
What I have:
What I need:
Group
Maria
Pedro
average
total
First
4
6
5
10
Second
5
10
7.5
15
Regards
Following the example detailed in the sample data table, to get the Total you could add the following measure;
Total By Group = CALCULATE( SUM(AverageExample[Maria]) + SUM(AverageExample[Pedro]))
and to average
Average By Group = [Total By Group] / 2
Based on the first three columns, this will provide
You have to build a DAX table (or Power Query) and a designated measure.
Matrix Table =
UNION(
DATATABLE("Detail", STRING, "Detail Order", INTEGER, "Type", STRING, {{"Average", 1000, "Agregate"}, {"Total", 1001, "Agregate"}}),
SUMMARIZE('Your Names Table', 'Your Names Table'[Name], 'Your Names Table'[Name Order], "Type", "Names")
)
This should give you a table with the list of people and 2 more lines for the agregations.
After that, you create a measure using variables and a switch function.
Matrix Measure =
var ft = FIRSTNONBLANK('Matrix Table'[Type], 0)
var fd = FIRSTNONBLANK('Matrix Table'[Detail], 0)
return SWITCH(TRUE,
ft = "Names", CALCULATE([Total], KEEPFILTERS('Your Names Table'[Name] = fd)),
fd = "Total", [Your Total Measure],
fd = "Average", [Your Averagex Measure]
)
The rest is up to you to fiddle with orders, add any agregate measures and whatnot.
Note that the Matrix Table should have no relation with any table from your model.
You can also hide it and the Matrix measure.
I'm new to crystal reports. I'm creating a bill report for clothing store application.
I'm having data like
prodID prodName qty rate amount salesmanID
101 saree 1 500 500 5
108 Legging 1 500 500 7
I want to display report as,
prodID prodName qty rate amount
101 saree 1 500 500
108 Legging 1 500 500
sid : 5 7
How do I achieve this?
In short: use shared variables to collect the salespersonIds within the detail section of your report, and print content of the shared variable at the end of it.
In detail:
Add one formula field to your report and drag it into the detail section. The formula should read like this (replace field name {Befehl.salesmanID}):
// #AddToArrayVar
whileprintingrecords;
numbervar array salespersonArray;
numbervar counter;
// add only new lines
if not({Befehl.salesmanID} in salespersonArray) then
(
counter := counter + 1;
//The line below ensures that the size of the array does
//not exceed 1000 values. An array can contain a maximum
//of 1000 values.
if counter <= 1000
then (
Redim Preserve salespersonArray[counter];
salespersonArray[counter] := {Befehl.salesmanID}
)
);
The formula field must be part of the detail section, you can hide it via field format.
Then add a second formula field for printing the array content and place it in a section after the details, e.g. report footer:
// #PrintFromArrayVar
whileprintingrecords;
numbervar array salespersonArray;
numbervar Counter;
stringvar salespersonslist;
numbervar i;
for i := 1 to Counter do
(
if i > 1 then salespersonslist := salespersonslist + ',';
salespersonslist := salespersonslist + ToText(salespersonArray[i],0,'');
);
salespersonslist;
P.S.: I'm not sure if the 1000 item limitation is still there in current version of Crystal Reports.
I have a data table that contains transactions by supplier. Each row of data represents one transaction. Each transaction contains a "QTY" column as well as a "Supplier" column.
I need to rank these suppliers by the count of transactions (Count of rows per unique supplier) then by the SUM of the "QTY" for all of each supplier's transactions. This needs to be in 1 rank formula, not two separate rankings. This will help in breaking any ties in my ranking.
I have tried dozens of formulas and approaches and can't seem to get it right.
See below example:
Suppliers ABC and EFG each have 4 transactions so they would effectively tie for Rank 1, however ABC has a Quantity of 30 and EFG has a QTY of 25 so ABC should rank 1 and EFG should rank 2.
Can anyone assist?
https://i.stack.imgur.com/vCsCA.png
Welcome to SO. You can create a new calculated column -
Rank =
var SumTable = SUMMARIZE(tbl, tbl[Supplier], "CountTransactions", COUNT(tbl[Transaction Number]), "SumQuantity", SUM(tbl[Quantity]))
var ThisSupplier = tbl[Supplier]
var ThisTransactions = SUMX(FILTER(SumTable, [Supplier] = ThisSupplier), [CountTransactions])
var ThisQuantity = SUMX(FILTER(SumTable, [Supplier] = ThisSupplier), [SumQuantity])
var ThisRank =
FILTER(SumTable,
[CountTransactions] >= ThisTransactions &&
[SumQuantity] >= ThisQuantity)
return
COUNTROWS(ThisRank)
Here's the final result -
I'm curious to see if anyone posts an alternative solution. In the meantime, give mine a try and let me know if it works as expected.
I have a dataframe, df1, that reports courses students have taken, where ID is the student’s id, COURSES is a list of courses taken by the student, and TYPE and MAJOR are student attributes. The dataframe looks like this:
ID COURSES TYPE MAJOR
1 ['Intr To Archaeology', 'Statics', 'Circuits I…] Freshman EEEL
2 ['Signals & Systems I', ‘Instrumentation’…] Transfer EEEL
3 ['Keyboard Competence', 'Elementary … ] Freshman EEEL
4 ['Cultural Anthro', 'Vector Analysis’ … ] Freshma EEEL
I created a new dataframe, df2, that reports a dissimilarity measure for each pair of students based on the courses they’ve taken. df2 looks like this:
I created using the following script, but it runs very slowly (there are thousands of students). Can someone suggest a more efficient way to create df2?
One major problem is that the script below calculates the distance between (student 1 and student 2) and (student 2 and student 1), which is redundant since the distances are the same. However, the condition I created to prevent this:
if (id1 >= id2):
continue
doesn't work.
Entire script:
for id1, student1 in df.iterrows():
for id2, student2 in df.iterrows():
if (id1 >= id2):
continue
ID_1 = student1["ID"]
ID_2 = student2["ID"]
# courses as list strings
s1 = student1["COURSES"]
s2 = student2["COURSES"]
try:
# courses as sets
courses1 = set(ast.literal_eval(s1))
courses2 = set(ast.literal_eval(s2))
distance = float(len(courses1.symmetric_difference(courses2)))/(len(courses1) + len(courses2))
except:
# Some strings seem to have a different format
distance = -1
ID_1_Transfer = 1 if student1["TYPE"] == "Transfer" else 0
ID_2_Transfer = 1 if student2["TYPE"] == "Transfer" else 0
df2= df2.append({'ID_1': ID_1,'ID_2': PIDM_2,'Distance': distance, 'ID_1_Transfer': ID_1_Transfer, 'ID_2_Transfer': ID_2_Transfer}, ignore_index=True)
Here is a data dump of what I am trying to sort
array
1
struct
col 1
dataid 48
identifier 1
row 1
size_x 4
size_y 1
2
struct
col 1
dataid 42
identifier 2
row 2
size_x 2
size_y 1
3
struct
col 3
dataid 45
identifier 3
row 2
size_x 2
size_y 1
I want to sort by row first, then col. Lots of examples how to sort by one data element, but none that talk about secondary elements.
ColdFusion 10 has built-in customised array sorting using a callback. The docs for arraySort() didn't mention this, but I've just updated them with an example. My example there doesn't show a compound sort like you require, but it's easy enough:
<cfscript>
comparator = function(e1, e2){
e1.row += 0; // need to make sure it's not a string for the Java method call below
var rowCompare = e1.row.compareTo(e2.row + 0);
if (rowCompare !=0){
return rowCompare;
}
e1.col += 0;
return e1.col.compareTo(e2.col + 0);
};
data = [
{row=3, col=3}, {row=3,col=2}, {row=3, col=1},
{row=2, col=3}, {row=2,col=2}, {row=2, col=1},
{row=1, col=3}, {row=1,col=2}, {row=1, col=1}
];
writeDump(var=data);
arraySort(data, comparator);
writeDump(var=data);
</cfscript>
This leverages that CF numerics are java.lang.Double objects.
<cfscript>
//ColdFusion 10 only supports this new types of struct declaration
recordArr = [
{col: 1,dataid:48,identifier:1,row:1,size_x:4,size_y:1},
{col: 1,dataid:42,identifier:2,row:2,size_x:2,size_y:1},
{col: 3,dataid:45,identifier:3,row:2,size_x:2,size_y:1}
];
//ColdFusion 10 only supports this new queryNew() functionality
queryObj = queryNew("col,dataid,identifier,row,size_x,size_y",
"Integer,Integer,Integer,Integer,Integer,Integer",
recordArr);
</cfscript>
<!--- Here it comes our favourite cfquery tag. We can apply order by clause
as per our wish --->
<cfquery name="ordredResult" dbtype="query">
SELECT * FROM queryObj ORDER BY row ASC, col ASC
</cfquery>
<!--- Here is the expected result --->
<cfdump var="#ordredResult#">