Change value based on 'value label' for all variables - spss

I want to update some values of a variable based on the value label of that same variable:
In pseudo-code:
for V in var1 to var10:
if value label of V is 'x':
set value 99

First we'll create some fake data to demonstrate on:
data list list/a1 to a3 (3f1) notforuse (f1).
begin data
1,2,2,55
2,1,4,66
3,4,1,77
end data.
value labels
a1 1 'x1' 2 'x' 3 'x3'
/a2 1 'y1' 2 'a' 3 'b' 4 'x'
/a3 1 'n' 2 'x'
/notforuse 55 'nn' 66 "x".
Now to the actual task:
* first step is to create a list of variable labels.
dataset name origdata.
DATASET DECLARE vallabs.
OMS /SELECT TABLES /IF COMMANDS=['File Information'] SUBTYPES=['Variable Values']
/DESTINATION FORMAT=SAV OUTFILE='vallabs' VIEWER=NO.
DISPLAY DICTIONARY.
OMSEND.
dataset activate vallabs.
* you now have a full list of your actual variable labels. next step is to select the variables you want to work on, and the labels you want to work on.
*to select the relevant variables.
select if any(var1 , "a1", "a2", "a3").
* can alternatively use the following: .
select if char.index(var1, "a")>0.
*to set the new values for relevant labels:.
recode label ("x"=99)("y1"=999) into newval.
select if not missing(newval).
*now use the list to create a new syntax: .
cd 'yourpath\writeable directory'
write out='val lab syntax.sps' /"if ", var1, " = ", var2, " ", var1, " = ", newval, ".".
exe.
*now use the new syntax in the original data:.
dataset activate origdata.
insert file= 'val lab syntax.sps'.
exe.

Related

SPSS set lables equal to value in column

As the title says.
What I'm trying to do is a way to set the labels of a column equal to the value in another column.
A B
1 Car
2 Bike
3 Van
1 Car
3 Van
Column A contains the numeric values. Column B contains the labels.
I want to tell SPSS to take the value 1, and assign it the label "Car" (and so on) as clasically is done manually with:
VALUE LABELS
1 "Car"
2 "Bike"
3 "Van".
Execute.
The syntax below will automatically create a new syntax that adds the value labels as you described.
Before starting, I'm recreating the sample data you posted to demonstrate on:
data list list/A (f1) B (a10).
begin data
1 "Car"
2 "Bike"
3 "Van"
1 " Car"
3 "Van"
end data.
dataset name orig.
Now we get to work:
* first we aggregate the data to get only one line for every value/label pair.
dataset declare agg.
aggregate out=agg /break A B /nn=n.
dataset activate agg.
* now we use the data to create a syntax file with the value label commands.
string cat (a50).
compute cat=concat('"', B, '"').
write out="yourpath\my labels syntax.sps" /"add value labels A ", A, cat, ".".
execute.
* getting back to the original data we can now execute the syntax.
dataset activate orig.
insert file="yourpath\my labels syntax.sps".

Google Sheets: How do I get a player's name to move with sorting of numbers from another column?

Complete novice here so please bare with me.
Set up: Column B has the points each player has won. Column A has the players' names. I set Column B by so that each players' cumulative points are auto-sorted in descending order (creating a player's ranking). As players' points are added, Column B sorts automatically.
What I can't figure out is how to get the players' names to move with their associated points when the rank is updated. Right if B1 is auto-sorted to B4, the end results is A1-B4. I need it to be A4-B4.
Hope that makes sense. Thanks!
If you have column A with names and column B with points. (With headers in first row)
Then paste this =sort(A2:B, 2, FALSE) into D2.
The A2:B is selecting columns A and B.
The 2 is the column that is used for sorting.
And FALSE for descending order (TRUE for ascending)
You will get a new sorted list.
Example output:
Name | Points Name | Points
Paul | 3 Peter| 10
John | 6 John | 6
Peter| 10 Paul | 3
You can use an Apps Script onEdit trigger if you don't want to add more columns and sort A and B directly:
Open the script editor by clicking Tools > Script editor.
Copy this function and save the project:
function onEdit(e) {
const sheet = SpreadsheetApp.getActiveSheet();
const range = sheet.getRange(2, 1, sheet.getLastRow() - 1, 2);
const values = range.getValues();
if (e.range.getColumn() == 2 && e.range.getRow() > 1) {
values = values.sort((a, b) => b[1] - a[1]);
range.setValues(values);
}
}
Now, every time column B is edited, both A and B are sorted.
Reference:
Simple Triggers

Generate a list of all unique values of a multi-column range and give the values a rating according to how many times they appear in the last X cols

As the title says.
I have a range like this:
A B C
------ ------ ------
duck fish dog
rat duck cat
dog bear bear
What I want is to get a single-column list of all the unique values in the range, and assign them a rating (or tier) according to the number of times they have appeared in the last X columns (more columns are constantly added to the right side).
For example, let's say:
Tier 0: hasn't appeared in the last 2 columns.
Tier 1: has appeared once in the last 2 columns.
Tier 2: has appeared twice in the last 2 columns.
So the results should be:
Name Tier
------ ------
duck 1
rat 0
dog 1
fish 1
bear 2
cat 1
I was able to generate a list of unique values by using:
=ArrayFormula(UNIQUE(TRANSPOSE(SPLIT(CONCATENATE(B2:ZZ9&CHAR(9)),CHAR(9)))))
But it's the second part that I am not sure exactly how to achieve. Can this be done through Google Sheets commands or will I have to resort to scripting?
Sorry, my knowledge is not enough to build an array-formula but I can explain how I get it per cell and then expanded a range from it.
Part 1: count the number of nonempty columns (assuming that if column has something on the second row, then it's filled.
COUNTA( FILTER( Sheet1!$B$2:$Z$2 , NOT( ISBLANK( Sheet1!$B$2:$Z$2 ) ) ) )
Part 2: build a range for the last two filled columns:
OFFSET(Sheet1!$A$2, 0, COUNTA( ... )-1, 99, 2)
Part 3: use COUNTIF to count how many values of "bear" we meet there (here we can pass a cell-reference instead) :
COUNTIF(OFFSET( ... ), "bear")
I built a sample spreadsheet that gets the results, here's the link (I know external links are bad, but there's no other choice to show the reproducible example).
Sheet1 contains the data, Sheet2 contains the counts.
I suggest using both script and the formula.
Normalize the data
Script is the easiest way to normalize data. It will convert your columns into single column data:
/**
* converts columns into one column.
*
* #param {data} input the range.
* #return Column number, Row number, Value.
* #customfunction
*/
function normalizeData(data) {
var normalData = [];
var line = [];
var dataLine = [];
// headers
dataLine.push('Row');
dataLine.push('Column');
dataLine.push('Data');
normalData.push(dataLine);
// write data
for (var i = 0; i < data.length; i++) {
line = data[i];
for (var j = 0; j < line.length; j++) {
dataLine = [];
dataLine.push(i + 1);
dataLine.push(j + 1);
dataLine.push(line[j]);
normalData.push(dataLine);
}
}
return normalData;
}
Test it:
Go to the script editor: Tools → Editor (or in Chrome browser: [Alt → T → E])
After pasting this code into the script editor, use it as simple formula: =normalizeData(data!A2:C4)
You will get the resulting table:
Row Column Data
1 1 duck
1 2 fish
1 3 dog
2 1 rat
2 2 duck
2 3 cat
3 1 dog
3 2 bear
3 3 bear
Then use it to make further calculations. There are a couple of ways to do it. One way is to use extra column with criteria, in column D paste this formula:
=ARRAYFORMULA((B2:B>1)*1)
it will check if column number is bigger then 1 and return ones and zeros.
Then make simple query formula:
=QUERY({A:D},"select Col3, sum(Col4) where Col1 > 0 group by Col3")
and get the desired output.

Formulate GSheets formula involving nested IFs

I have created a Google spreadsheet for our small business which lists all the invoices. I have uploaded a simplified format in
https://docs.google.com/spreadsheets/d/1zYrRxDm0ahsjWE8aNquz-shHuNY_Eifl3lXLhIBUeTE/edit?usp=sharing.
1.There can be 1-5 products per invoice.
2.The column G is the total of all the products in that invoice. I want to create a formula for this column.
Presently, my formula is very long and inefficient.
The column (G) calculates number of products with this formula:
=IF(B3<>"",IF(OFFSET(B3,1,0)="",IF(OFFSET(B3,2,0)="",if(OFFSET(B3,3,0)="",if(OFFSET(B3,4,0)="",if(OFFSET(B3,5,0)="",5,5),4),3),2),1),0)
Another column (H) sums up the product values with this: =IF(G3>0,SUM(OFFSET(D3,0,0,G3,1)),"")
Help me rework the G column formula which calculates the number of products. If there's any way I can consolidate G and H that would be great too.
Note: the (I) column is just an alternative to (H) column.
P.S. Please don't flag this as an opinion based question. This is purely a problem solving question.
Since you are ok with the option of a helper column off to the side or hidden, we can do the following.
In column K starting in row 3 I placed this formula:
=IF(A3<>"",A3,K2)
You can actually use whatever column suits you just remember to update the column references in subsequent formulas. It generates a column of invoice numbers with no spaces which allows some other formulas to work much easier for us.
In column L startin in row 3 I placed this formula:
IF(COUNTIF($K$3:K3,K3)=1,COUNTIF(K:K,K3),0)
This gives the same results as column G. The first part of the IF statement is checking to see if the invoice number is the first occurrence of the invoice number. If it is count how many times the invoice number occurs, otherwise display 0.
Now if you want to skip counting how many items there are in an invoice you can use the sumproduct formula as follows:
=IF(A3<>"",SUMPRODUCT(($K$3:$K$12=A3)*$D$3:$D$12),"")
now to account for a variable sized list of invoices we will count the number of invoices and adjust our formula with an offset to return the appropriate ranges as follows:
=IF(A3<>"",SUMPRODUCT((OFFSET($K$3,0,0,COUNT(K:K),1)=A3)*OFFSET($D$3,0,0,COUNT(K:K),1)),"")
Since we are using COUNT(K:K) it is imperative that no numbers be entered in this column other than those generated by our formula.
This treats items inside the brackets as an array, without the formula itself being an array. The whole thing is placed inside an IF statement so that empty cells are displayed instead of zeros in the rows that do not correspond to an invoice number in column A.
now if you want to understand how sumproduct works in this case, its basically generating a an array filled with 1 or 0 representing true or false and then multiplying it by an array of the same size that is filled with all your amounts. So anything multiplied by 0 is 0 and anything multiplied by 1 is amount. The final step of sumproduct is to add up all the values. So you will only get the sum of what ever is true or 1.
If you are able to utilise VBA, you could use a User Defined Function. Insert this code into a new module and call it like you would a normal excel function:
Public Function InvoiceDetail(Invoice As Range, ReturnType As Integer)
Dim varCount As Long
Dim varSheet As Worksheet
Dim varInvoiceID As String
Dim varPartyName As String
Dim varInvoiceTotal As Double
Dim varInvoiceCount As Integer
Set varSheet = ThisWorkbook.Sheets(Invoice.Parent.Name)
If varSheet.Range("A" & Invoice.Row).Value <> "" Then
varInvoiceID = varSheet.Range("A" & Invoice.Row).Value
varPartyName = varSheet.Range("B" & Invoice.Row).Value
For varCount = Invoice.Row To 1000000
If varSheet.Range("D" & varCount).Value = "" Then
Exit For
End If
If varInvoiceID = varSheet.Range("A" & varCount).Value And varPartyName = varSheet.Range("B" & varCount).Value Then
varInvoiceTotal = varInvoiceTotal + varSheet.Range("D" & varCount).Value
varInvoiceCount = varInvoiceCount + 1
ElseIf varSheet.Range("A" & varCount).Value = "" And varSheet.Range("B" & varCount).Value = "" Then
varInvoiceTotal = varInvoiceTotal + varSheet.Range("D" & varCount).Value
varInvoiceCount = varInvoiceCount + 1
Else
Exit For
End If
Next
End If
Set varSheet = Nothing
Select Case ReturnType
Case 1 '// Count Only
InvoiceDetail = varInvoiceCount
Case 2 '// Total Only
InvoiceDetail = varInvoiceTotal
Case 3 '// Total [Count]
InvoiceDetail = varInvoiceTotal & " [" & varInvoiceCount & "]"
Case Else
InvoiceDetail = "Error"
End Select
End Function
This code obviously assumes that your Invoice ID is in Column A, your Party Name is in Column B, and your Amount is in Column D. I've implemented a few options for you too:
=InvoiceDetail(A3,1) returns the number of items on the invoice (as integer)
=InvoiceDetail(A3,2) returns the sum of items on the invoice (as double)
=InvoiceDetail(A3,3) returns both sum and [count] (as string)
I was able to solve this with 2 arrayformulas.
Paste this formula in any corresponding cell, let it be O3:
=TRANSPOSE(SPLIT(JOIN("",ArrayFormula(REPT(FILTER(A3:A12,A3:A12>0)&"-",
len(TRANSPOSE(SPLIT(REGEXREPLACE(JOIN(",",A3:A12)&",","\d+","-"),"-")))))),"-"))
And this formula in cell P3:
=ArrayFormula(if(A3:A>0,SUMIF(O3:O,O3:O,D3:D),""))
My sample file.
To make the first formula work with different ranges:
replace A3:A12 to offset(A3,,,counta(C3:C))

SPSS String Column Split into 2

I have a column with names and middles names separated by space. Is there a command that will let me put everything after space into a new column? (ie first names and middle names each in their own columns)
I am using SPSS.
Thanks!
Assuming the variable to split is called Name, try:
string first middle (a50).
compute #x = char.index(Name, " ") > 0.
do if #x = 1.
compute first = char.substr(Name, 1, char.index(Name, " ")).
compute middle = char.substr(Name, char.index(Name, " ")).
else.
compute first = Name.
end if.
exe.

Resources