I am trying to produce a table like the one depicted below using the "Custom Tables" option in SPSS.
FREQUENCIES Descriptive
Completely.Disagree Disagree Agree Compl.Agree mean std mode n
item1 count count count count
n% n% n% n%
item2 count count count count
n% n% n% n%
I am able to produce the first part of the table (FREQUENCIES) by putting the variables as rows and the categories as columns and then selecting the summary statistics (count, n%). However, I can not find a way to put the descriptive statistics part in this table. Is there a way to do it? Or maybe combine the output of the custom tables for the FREQUENCIES PART with the descriptive statistics output using "Descriptive Statistics" option in one table?
Your question is difficult, because you want to two things that are not combinable in one table:
1 You want the same category labels for different variables to be transferred to the columns
2 You want descriptives of continuous variables next to (1).
This is the closest i could come:
compute x=1
cta
/vla var=x var1 disp=none
/tab x by var1 [c][count 'Count'][layerpct 'n%'] + var1 [s][mean][mean][stddev][mode][validn]
/sla pos=column
/cla rowlabel=layer.
You would have to repeat this for every variable (var1, var2 etc) and each time take out the row with the figures you need. Furthermore, a 1 would be visible above each table and there would be an empty column on the left.
What I would suggest is making two tables: one for the counts and percentages and one for the mean, stddev etc. You would do this with:
cta
/tab (var1 + var2)[c][count 'Count'][rowpct 'n%']
/cla rowlabel=opposite
/sla pos=row.
You can leave out the /sla pos=row, whichever you prefer.
And then the second table:
cta
/tab (var1 + var2)[s][mean][stddev][mode][validn].
Good luck!
Related
I have 3 sheets:
Sheet1 - list of transactions (Account, Credit, Debit, Date)
Sheet2 - list of transactions (Account, Credit, Debit, Date)
Sheet3 (I plan to lock it) - combined list of transactions, sorted by Date
Sheet3 looks like:
I need to add 1 more column to Sheet3 to count current balance for certain row to be like:
I'm able to do this with formula:
=SUM(FILTER($B$2:$B$8, ROW($A$2:$A$8) <= ROW($A2), A$2:A$8=$A2)) - SUM(FILTER($C$2:$C$8, ROW($A$2:$A$8) <= ROW($A2), A$2:A$8=$A2))
But this one I need continuously drag down.
Question: Is there way convert this formula to ArrayFormula, to avoid dragging
In G2 on sheet 3 I entered
=ArrayFormula(if(A2:A="",,mmult((A2:A=transpose(A2:A))*(row(A2:A)>= TRANSPOSE(row(A2:A)))*(transpose(B2:B)-transpose(C2:C)),row(A2:A)^0)))
See if that works for you?
In Sheet3 row 1, put your headers.
In Sheet3!A2, put
=sort({filter(Sheet1!A2:D,not(isblank(Sheet1!A2:A)));filter(Sheet2!A2:D,not(isblank(Sheet2!A2:A))),4,true)
In Sheet3!E2, put
=mmult(transpose(arrayformula(arrayformula(array_constrain(A2:A,counta(A2:A),1)=transpose(array_constrain(A2:A,counta(A2:A),1)))
*arrayformula(array_constrain(row(A2:A),counta(A2:A),1)<=transpose(array_constrain(row(A2:A),counta(A2:A),1))))),
arrayformula(array_constrain(B2:B,counta(A2:A),1)-array_constrain(C2:C,counta(A2:A),1))
To see why, let's temporarily remove the array_constrain(...,counta(...),1) wrappings, which is meant to auto detect the last data row:
=mmult(transpose(arrayformula(arrayformula(A2:A9=transpose(A2:A9))
*arrayformula(row(A2:A9)<=transpose(row(A2:A9))))),
arrayformula(B2:B9-C2:C9))
arrayformula(B2:B9-C2:C9) are the running sums of column B - column C (ie. credit - debit). It is a column vector with the length of your data size.
We want to, for each row, 1) filter this vector by comparison to column A (ie. account name) & 2) filter this vector by whether the running sums are below or above the row in question.
arrayformula(A2:A9=transpose(A2:A9)) does 1). arrayformula(row(A2:A9)<=transpose(row(A2:A9))) does 2).
We want elementwise product between the 2 matrices in order to compose the filter. Hence, arrayformula(...*...).
The columns of our filters are meant to be applied to the running sums. To use matrix multiplication, we can keep the column vector of running sums as the post-multiplier; and transpose the filter matrix as pre-multiplier so that the rows of the transposed matrix are multiplied (ie. applied) to the running sums. Hence, mmult(transpose(...),...).
Add back the array_constrain trick. And we are done.
Feel free to experiment with alternate placings of arrayformula. But remember to keep the () brackets wherever you omit arrayformula. Example:
=arrayformula(mmult(transpose(((array_constrain(A2:A,counta(A2:A),1)=transpose(array_constrain(A2:A,counta(A2:A),1)))
*(array_constrain(row(A2:A),counta(A2:A),1)<=transpose(array_constrain(row(A2:A),counta(A2:A),1))))),
(array_constrain(B2:B,counta(A2:A),1)-array_constrain(C2:C,counta(A2:A),1))))
Nonetheless, the 1 formula solution is computationally inefficient compared to individually spread formula per cell. That is because, without mutating the formula per row, we are forced to compute the filters as full n-by-n matrices where n is your data size.
Whereas, if in E2 we put =sum(filter(B$2:B2-C$2:C2,A$2:A2=A2)) and spread to the end by double right-clicking the square on bottom right when you select E2, the formula mutates per row, saving the row index comparison entirely, and also cutting the comparison to column A logarithmically.
Granted, we probably shouldn't rely on Google Sheet for a large database (e.g. >100k entries). But even for thousands of entries, if you square the amount of computations required, getting the results in browser becomes impractically slow well before one may expect.
My intention is to convert a single line of data into rows consist of a specific number of columns in Google Sheets.
For example, starting with the raw data:
A
B
C
D
E
F
1
id1
attr1-1
attr2-1
id2
attr2-1
attr2-2
And the expected result is:
(by dividing columns by three)
A
B
C
1
id1
attr1-1
attr1-2
2
id2
attr2-1
attr2-2
I already know that it's possible a bit manually, like:
=ARRAYFORMULA({A1:C1;D1:F1})
But I have to start over with it every time the target range is moved OR the subset size needs to be changed (in the case above it was three)!
So I guess there will be a much more graceful way (i.e. formula does not require manual update) to do the same thing and suspect ARRAYFORMULA() is the key.
Any help will be appreciated!
I added a new sheet ("Erik Help") where I reduced your manually entered parameters from two to one (leaving only # of columns to be entered in A2).
The formula that reshapes the grid:
=ArrayFormula(IFERROR(VLOOKUP(SEQUENCE(ROUNDUP(COUNTA(7:7)/A2),A2),{SEQUENCE(COUNTA(7:7),1),FLATTEN(FILTER(7:7,7:7<>""))},2,FALSE)))
SEQUENCE is used to shape the grid according to whatever is entered in A2. Rows would be the count of items in Row 7 divided by the number in A2 (rounded to the nearest whole number); and the columns would just be whatever number is entered in A2.
Example: If there are 11 items in Row 7 and you want 4 columns, ROUNDUP(11/4)=3 rows to the SEQUENCE and your requested 4 columns.
Then, each of those numbers in the grid is VLOOKUP'ed in a virtual array consisting of a vertical SEQUENCE of ordered numbers matching the number of data pieces in Row 7 (in Column 1) and a FLATTENed (vertical) version of the Row-7 data pieces themselves (in Column 2). Matches are filled into the original SEQUENCE grid, while non-matches are left blank by IFERROR
Though it's a bit messy, managed to get it done thanks to SEQUENCE() function anyway.
It constructs a grid by accepting number of rows/columns input, and that was exactly I was looking for.
For reference set up a sheet with the sample data here:
https://docs.google.com/spreadsheets/d/1p972tYlsPvC6nM39qLNjYRZZWGZYsUnGaA7kXyfJ8F4/edit#gid=0
Use a custom formula
Although you already solved this. If you are doing this kind of thing a lot, it could be beneficial to look into Apps Script and custom formulas.
In this case you could use something like:
function transposeSingleRow(range, size) {
// initialize new range
let newRange = []
// initialize counter to keep track
let count = 0;
// start while loop to go through row (range[0])
while (count < range[0].length){
// add a slice of the original range to the new range
newRange.push(
range[0].slice(count, count + size)
);
// increment counter
count += size;
}
return newRange;
}
Which works like this:
The nice thing about the formula here is that you select the range, and then you put in a number to represent its throw, or how many elements make up a complete row. So if instead of 3 attributes you had 4, instead of calling:
=transposeSingleRow(A7:L7, 3)
you could do:
=transposeSingleRow(A7:L7, 4)
Additionally, if you want this conversion to be permanent and not dependent on formula recalculation. Making it in run fully in Apps Script without using formulas would be neccesary.
Reference
Apps Script
Custom Functions
Hi everyone,
I have 4 ranges of data:
A1:C5 (Product In Storage), there will be new data input if there are other new products coming in.
E1:G8 (Sales), this is the record for all the products that are being sold, there will be new data input also in the future.
I1:K5 (Summary of Sales), this will record the Quantity Left in the storage and also the total Profit or Loss after doing some calculation based on A1:C5 and E1:G8. The number of rows for Summary of Sales should be equal to the number of rows of Product In Storage (So I'm using query function highlighted in yellow).
M1:O5 (Desired Output), this is the final result that I want to achieve for this task.
My goal is to calculate the Quantity Left & Profit or Loss in Summary of Sales automatically once there are new data input in Product In Storage & Sales.
I'm not sure how to do it because the number of rows in Sales is a not a constant. In this example, the are 2 rows data for Product ID: 1001.01 & 3 rows data for Product ID: 1002.01. In the future, after the remaining quantity (34) for Product ID: 1001.01 had been sold, it need to be included in the Summary of Sales as well.
I attached the google sheet link below in case you need it: https://docs.google.com/spreadsheets/d/17coke3-oyDRLHgz79PDl3KX68kFOEte-aynVe-xEITU/edit?usp=sharing
Is there anyway to do this without using the google apps script? Any help will be greatly appreciated!
For your example,
J4 -> =SUMIF(A:A,I4,C:C) - SUMIF(E:E,I4,G:G) means;
(First sumif, get inventory) Whenever you see I4 in A:A column, get the C value on that row and sum all of them.
(And minus)
(Second sumif, minus sales) Whenever you see I4 in E:E column, get the G value on that row and sum all of them.
(Result) = Get storage quantity and substitute the sales quantity.
K4 -> =ArrayFormula(SUMPRODUCT(IF(E:E=I4,F:F*G:G)) - SUMPRODUCT(IF(A:A=I4,B:B*C:C)))
means,
(get income) if you see I4 on E:E column, then get the sum of (F*G)
(minus)
(get expense) if you see I4 on A:A column, then get the sum of (B*C)
But if i were you, to keep things more simple, i would put =Quantity*Price right of your tables and you can solve problems with only =SUMIF formulas. FYI, this will update everytime you change a value from table so when things get nastier, it will become slower indeed. When that time comes, a VBA macro button or Formulas->Calculation method will save your day.
As you can see I transpose codes into unique column headings so that debits and credits are analysed and summated. Summations are transposed in another sheet to create summary profit/loss account. I need help how to replicate the sum formula in column I to serve any expanded transposed unique codes and whether/how I should use arrayformula for the individual cell output.
EDIT
Actual output looks like this:
My problem is to how to automatically accommodate new entries/codes in the totals row and main body of cells. The data belongs to a residents' committee so I can only show anonymous data as image.
EDIT 2
Actual input is imported from bank records, then coded:
Query is pretty good for the SUM part.
Starting in column I, you can do:
=ArrayFormula(INDEX(QUERY(
0+OFFSET(I4,0,0,ROWS(F6:F),COUNTA(UNIQUE(F4:F))),
"select "&
JOIN(
",",
"sum(Col"&SEQUENCE(COUNTA(UNIQUE(F4:F)))&")"
)
),2))
The 0+ or the VALUE in the second one (they both do the same thing here) transforms the data cells to default to 0 if blank, otherwise the query fails. This also lets us refer to the columns by sequence number, which is what we do in the second argument. We build the query into something that looks like select sum(Col1),sum(Col2),...,sum(ColN). Since this gives us a header by default, we could relabel everything in the query statement, but that gives too much extra code, so the easier thing to do is use INDEX to select the sums.
The EQ part is fairly straightforward to Arrayify. Starting in I4:
=ArrayFormula(
(FILTER(F4:F,F4:F<>"")=FILTER(I2:2,I2:2<>""))*
IF(
Array_constrain(G4:G,COUNTA(FILTER(F4:F,F4:F<>"")),1),
G4:G,
-H4:H
)
)
The FILTERs just filter out the blank cells, and the Array_Constrain sizes the G column to the same size as the filtered F column.
We have asked users:
What to do with the money?
[ ] paint the bridge
[ ] rebuild the school
[ ] keep the money
[ ] Other : [____________________]
Here is the spreadsheet with their answers:
A B
1 Name Choices
2 Lilia paint the bridge, rebuild the school, keep the money
3 Paul rebuild the school, paint the bridge, do something else
4 Margerite keep the money, I don't know, do what you want
5 John paint the bridge
...
800
I want a formula that output the number of official choices (excluding other) picked per user.
With the first 4 rows of data, the formula would output this table:
D E
Nbr of choices a user made Frequency (Nbr of users who made these choices)
0 0
1 2
2 1
3 1
Couldn't find a way to get this right from a single formula. For a starter, I wanted to split each line (of B2: B) by "," but couldn't find a way to apply a fn (split) to each line in an formula...
Even with 800 rows of data (B2:B), the resulting table (D2:E5) would always be 4 rows long plus titles (and two column wide)
I could do this in C2, and replicate manually with the "+" corner icon...
=countif(B2;"*rebuild the school*")+countif(B2;"*keep the money*")+countif(B2;"*paint the bridge*")
And then do in E2:
=arrayformula(countif(C2:C;D2:D5))
But I'd like to generate the table of frequencies in one formula, without any manual action (without C column).
So I am looking for a way to "map" the first function to each row, put this in the second fn.
ANSWER by Akshin Jalilov EXPLAINED
This is the answer by Akshin Jalilov, but shorter (and with international notations)
=ARRAYFORMULA(COUNTIF(ARRAYFORMULA(IF(B2:B="";;COUNTIF(ARRAYFORMULA
(IFERROR(IF(FIND("paint the bridge";B2:B);Row(B2:B);0)));"="&row(B2:B))
+COUNTIF(ARRAYFORMULA(IFERROR(IF(FIND(
"rebuild the school";B2:B);Row(B2:B);0)));"="&row(B2:B))
+COUNTIF(ARRAYFORMULA(IFERROR(IF(FIND(
"keep the money";B2:B);Row(B2:B);0)));"="&row(B2:B))));"="&D2:D5))
Step1:
IF(FIND("rebuild the school";B2:B);Row(B2:B);0)
This means, for each row (B2:B) find "rebuild the school". If you find it, return the number of the row, otherwise, return 0.
Step2:
=ARRAYFORMULA(IFERROR(Step1))
Wrap this in an ARRAYFORMULA so that you return the results for each row.
I think IFERROR is there to prevent an error from stopping the process.
Step3:
=ARRAYFORMULA(IF(B2:B="";;COUNTIF(ARRAYFORMULA(IFERROR(IF(FIND("paint the bridge";B2:B);Row(B2:B);0)));"="&row(B2:B))+countif(Step2)+countif(ARRAYFORMULA(IFERROR(IF(FIND("keep the money";B2:B);Row(B2:B);0)));"="&row(B2:B))))
This will count valid votes made by each users. This is equivalent to C2 formula referred in my manual process. But is it now part of a single global formula.
Step4:
Lastly, the rest of the formula counts frequencies of each voting count possibilities.
I know this formula is large but this is the closest I got to what you want.
Now to make it easy, name your responses range "Responses". I assume it is B2:B.
Here is the formula:
=ARRAYFORMULA(Countif(ARRAYFORMULA(IF(Responses="",,COUNTIF(VLOOKUP(row(Responses),({ARRAYFORMULA(Row(Responses)),ARRAYFORMULA(IFERROR(IF(FIND("paint the bridge",Responses),Row(Responses),0))),ARRAYFORMULA(IFERROR(IF(FIND("rebuild the school",Responses),Row(Responses),0))),ARRAYFORMULA(IFERROR(IF(FIND("keep the money",Responses),Row(Responses),0)))}),2),"="&row(Responses))+COUNTIF(VLOOKUP(row(Responses),({ARRAYFORMULA(Row(Responses)),ARRAYFORMULA(IFERROR(IF(FIND("paint the bridge",Responses),Row(Responses),0))),ARRAYFORMULA(IFERROR(IF(FIND("rebuild the school",Responses),Row(Responses),0))),ARRAYFORMULA(IFERROR(IF(FIND("keep the money",Responses),Row(Responses),0)))}),3),"="&row(Responses))+COUNTIF(VLOOKUP(row(Responses),({ARRAYFORMULA(Row(Responses)),ARRAYFORMULA(IFERROR(IF(FIND("paint the bridge",Responses),Row(Responses),0))),ARRAYFORMULA(IFERROR(IF(FIND("rebuild the school",Responses),Row(Responses),0))),ARRAYFORMULA(IFERROR(IF(FIND("keep the money",Responses),Row(Responses),0)))}),4),"="&row(Responses)))),"="&D2:D5))
Here is an example if how it works. I am not sure which one exactly you wanted so added both