SPSS: Inconsistent totals due to rounding of numbers - spss

I am using weights when running the data with SPSS custom tables.
Thus it is expected that the column or row values may not add up to row total, column total or Table Total due to rounding of decimals
sample table result:
variable 2
category 1 category 2 Total
variable 1 category 1 45 52 97
category 2 60 56 115
Total 105 107 211
Is there a way to force SPSS to output the correct row, column, or table totals?
expected table output:
variable 2
category 1 category 2 Total
variable 1 category 1 45 52 97
category 2 60 56 116
Total 105 108 213

If you are using the CROSSTABS procedure to produce these figures then you should do using the option ASIS.

To be clear: the total displayed by CTABLES is mathematically correct. However, if you want to display as the total the sum of the displayed values in the rows, instead, the only way to do this is by using the STATS TABLE CALC extension command to recompute the totals using the rounded values.
Here is how to do that.
First, you need to create a Python module named customcalc.py with the following contents
def custom(datacells, ncells, roworcol):
'''Calculate sum of formatted values'''
total = sum(float(datacells.GetValueAt(roworcol,i)) for i in range(ncells))
return(total)
This file should be saved in the python\lib\site-packages directory under your Statistics installation or anywhere else that Python can find it.
Then, after your CTABLES command, run this syntax
STATS TABLE CALC SUBTYPE="customtable" PROCESS=PRECEDING
/TARGET custommodule="customcalc"
FORMULA="customcalc.custom(datacells, ncells, roworcol)" DIMENSION=COLUMNS LEVEL = -2 LOCATION="Total"
LABEL="Rounded Count".
That custom function adds up the formatted values in each row instead of the full precision values. If you have suppressed the default statistic name, Count, so that "Total" is the innermost label, use LEVEL=-1 instead of LEVEL=-2 ABOVE.

Related

How to find the row of a specified value than return a corresponding value on a different column Google Sheets

I'm trying to write a formula in Google Sheets which can first locate the row of a specific value. Then index to the value contained on that row a few columns over.
Let's assume the following
A B C
1 12 80
2 43 35
3 64 15
4 13 56
5 44 93
6 86 48
7 14 31
8 41 3
9 63 56
10 11 46
Values in column B and C have a correlated relationship. I need to first locate a specific value in column B than find it's corresponding value on the same row in column C.
For the sake of example, let's assume I'm trying to locate the row containing the value 41 in column B. And then would like to return the corresponding value in column C, which in this case would be 3.
The reason why I need a formula like this is because the data I'm using is highly variable and large. Over 4000 rows. It is unknown what rows the values to be found sit on.
You may try either:
=filter(C:C,B:B=D2)
OR
=xlookup(D2,B:B,C:C,)
filter() will output all instances of rows(column C) which has 41 in column B while xlookup will pick just the first match of 41 within the column

Checking to which range a value belongs in Google Sheets

I have some data in the following way
Category
[Range 1_min]
[Range 1_max]
[Range 2_min]
[Range 2_max]
...
A
120
130
...
B
100
119
131
140
...
I want to be able to quickly query a number and have it return the category it belongs to, for example 135 belongs to B and 121 belongs to A.
I already have a script that does this, but since there are 1000+ categories, it takes a long time to run. Is there a faster way of doing this?
Thanks.
You can use LOOKUP:
=ArrayFormula(LOOKUP(2,1/((G2>=B2:B)*(G2<=C2:C)+(G2>=D2:D)*(G2<=E2:E)),A2:A))
Addition:
For more ranges you can add MMULT (not sure it's easier):
=ArrayFormula(LOOKUP(1,5/(MMULT(--(K2>={B2:B,D2:D,F2:F,H2:H}),ROW(A1:A4)^0)*MMULT(--(K2<={C2:C,E2:E,G2:G,I2:I}),ROW(A1:A4)^0)),A2:A))
some conditions:
change first argument of LOOKUP to 1
for second LOOKUP argument change denominator to 5 (number of cols to compare + 1)
for second MMULT argument ROW(A1:A4) use row count according column count to compare (i.e. for 4 cols ->ROW(A1:A4), for 6 cols -> ROW(A1:A6) etc. )

ARRAYFORMULA with repetition

I have two columns of data, and would like to distribute the elements of one of these columns over several rows. I can easily calculate the index of the element I need, but cannot figure out how to access the element.
A B Desired output Formula for index: =ARRAYFORMULA(IF(A:A,CEILING(ROW(A:A)/3+1),""))
1 11 22 2
2 22 22 2
3 33 22 2
4 44 33 3
5 33 3
6 33 3
7 44 4
How can I modify my formula for the index so that it yields the item of column B at the calculated index?
I tried =ARRAYFORMULA(IF(A:A, INDEX(B:B, CEILING(ROW(A:A)/3+1), 1), "")) but that only repeats the first element (22) 7 times.
Use Vlookup instead of Index:
=ARRAYFORMULA(IF(A:A,vlookup(CEILING(ROW(A:A)/3+1),A:B,2),""))
EDIT
It isn't necessary to use a key column, you could use something like this:
=ARRAYFORMULA(vlookup(CEILING(sequence(counta(B:B)*3)/3+1),{row(B:B),B:B},2))
assuming you wanted to generate three rows for each non-blank row in column B not counting the first one.
Or if you want to be different, use a concatenate/split approach:
=ArrayFormula(flatten(split(rept(filter(B:B,B:B<>"",row(B:B)>1)&"|",3),"|")))
(all the above assume you want to ignore the first row in col B and start with 22).

select less than and replace with value in column A

I have a table with a few thousand rows and columns, it looks sort of like this
this:
ID Distance1 Distance2
1 102 101
2 101 100
3 100 99
4 99 98
5 98 97
...
I would like to select all values/distances in columns B and C that are less than 100 and replace them with the value in column A (their ID number).
All distances above 100 I want to delete. The real table has several thousand columns. How can I do this?
I have tried using search and replace, and conditional formatting where I have tried creating new rule using Index + Match but I encounter errors.
Assuming ID is in A1 of Sheet1, Copy the headings row into A1 of a new sheet and in B2 of that sheet:
=IF(AND(Sheet1!B2<100,Sheet1!B2>0),Sheet1!$A2,"")
Copy across and down to suit, Select the new sheet, Copy, Paste Special, Values over the top.
This above treats 100 as more than 100 and assumes no 0 or lesser values.

Excluding the last value in a range from an aggregate calculation in Google Sheets

I have a Google Sheet with two columns of data. A is monotonically increasing with many duplicates (based on a coarse timestamp), while B is essentially random. There are many empty rows at the bottom waiting for future data. It resembles the following:
A B
1 5 43
2 5 77
3 13 8
4 21 34
5 27 68
6 27 90
7
8
9
10
I'm trying to write a few formulae which examine all of the (non-empty) values in a column except for the last one. For example, I would like to find the maximum value of B excluding the latest value, so the result should be 77 from B2 instead of 90 from B6.
If the values in the range were strictly increasing and unique, I could filter the values of A into C, excluding any values equal to the maximum value (only the last entry), and then take the MAX(..) of that range. However, my data does not have that property; the final value could be duplicated and the duplicates would be inappropriately ignored.
C D E
1 =FILTER(A:A, A:A < MAX(A:A)) =MAX(C:C) This produces A4's 21 instead of A5's 27.
A similar approach would work if we had a third column of incrementing indices to use:
A B C D E
1 5 43 9 =MAX(FILTER(C:C, A:A <> "")) Value of index in last populated row.
2 5 77 10 =MAX(FILTER(A:A, C:C < D1)) Maximum value from a row with lower index.
3 13 8 11
4 21 34 12
5 27 68 13
6 27 90 14
7 15
8 16
9 17
10 18
But I'm looking for a solution that doesn't require modifying the original spreadsheet, because that's not always possible. I can't just create a new IndexSheet with nothing but an an index column and join it in like this instead...
A B C
1 5 43 =MAX(FILTER(IndexSheet!A:A, A:A <> ""))
2 5 77 =MAX(FILTER(A:A, IndexSheet!A:A < C1))
...
...because that requires that the IndexSheet have the same number of rows as the data sheet, and would break as more data is added.
Without modifying the original data sheet, or relying on properties of the data (beyond values being numeric and rows being empty or full), is there any way to perform an aggregate calculation on a range while excluding the last value?
You can use indirect and address formulas to create dynamic range excluding the last row
=max(indirect("A1:"&Address(count(A:A)-1,1)))
The count function gives the number of non empty cells in the column A. You subtract 1 to exclude the last row.
You use that number to build an address using "A1:"&address(row no, Col no) which in your example case should be A1:$A$5
Use this string to reference your cells using the indirect method indirect(A1:$A$5) and pass the reference to the max function to determine the max in that range.
From another sheet try:
=MAX(Sheet1!B1:indirect("Sheet1!B"&count(Sheet1!B:B)-1))
We can use the FILTER() and ROW() functions to accomplish this:
D
1 =MAX(FILTER(Data!A:A,
ROW(Data!A:A) < MAX(FILTER(ROW(Data!A:A),
Data!A:A <> ""))))
We use FILTER(ROW(DATA!A:A), Data!A:A <> "")) to get an array of row numbers of non-empty rows, and use MAX(...) to take the last row number. We use this to exclude the last row by filtering out values from lower row numbers with FILTER(Data!A:A, ROW(Data!A:A) < ...). We apply MAX(...) to this filtered array and get the result we were looking for.

Resources