Automate Excel graphs with AutoHotKey and SPSS; Automating pretty charts!

Automate Excel graphsA white stress ball with the green AutoHotkey logo being held in a hand, with a laptop keyboard blurred in the backgroundAfter 20 years of doing multivariate statistics, doing analytics is very quick and easy for me.  Putting together the report which clients will think is “pretty” and convey the results is what really takes the time!  I have a ton of macros in SPSS which greatly simplify the front-end however I’ve always preferred Excel graphs to SPSS (because they don’t look like they were drawn with crayons).

Automate Excel graphs

In the below video I demonstrate how you how you can use AutoHotKey to easily automate Excel graphs that you’ll be proud to put your name on!

Two women smiling and looking at a computer screen together in a bright office, one pointing at the monitor.

Great use of GIS data to decide your next business location

GIS Data

Years ago I helped a company that worked in the financial industry review where their current locations are and where FDIC Banks were located.  The GIS data was available for free from the FDIC.  If you spend some time you can merged the files together and look at it longitudinally.   The below map was particularly helpful in deciding where a new branch/office should be located.  I also imported the locations of competitors however I cannot share this data as it is too revealing.

I used Microsoft’s MapPoint to create the below map.  It is no longer with us however this would be easy to repeat with Google Maps.

GIS data

Here’s a great book to get ideas on conveying data visually

Venn diagram comparisons of sets- quick way to see overlap or lack there of

venn diagram

Venn diagram comparisons of sets

I often need to determine what items are in set A and Set B, in Set A and not in Set B and in B and not in Set A. Excel and other tools can do this but it takes time to open it up and configure the file. I wrote an AutoHotKey script which is very easy to get the Union, etc. and is very flexible.   The script can be downloaded below but I recommend first watching the video to understand exactly what is going on.

 

 

Venn Diagram / Compare sets Utility

 

Venn Diagram / AutoHotkey Compare Sets code

 
#SingleInstance, Force
#NoEnv
SetBatchLines -1
DetectHiddenWindows, On
; Menu, tray, icon, B:\Progs\AutoHotKey_L\Icons\progs\Union.ico, 1
 
gosub Display
RAlt::
Browser_Forward::Reload
;~ RControl::
;~ Browser_Back::
 
return
 
 
Display:
;**********************gui*********************************
;~ gui, +toolwindow +alwaysontop +lastfound
;  look up XYHW or XYWH  (function on new forum)-  on old forum- Anchor
gui, +resize
gui, font, s11 bold, Verdana  ; Set 10-point Verdana.
Gui, add, Text,cRed  x20 y10  , A  ;Title for A
Gui, add, Text,cBlue x0  y460 , B  ;title for B
gui, font, ; reset to default
 
Gui, Add, Text,               cRed     x140  y15   R1 , Count: 
Gui, Add, Text, vA_Count_GUI  cRed     x180  y15   R1 , %A_Count% ;count A for update later
 
Gui, add, Edit, vA_Values cRed x0 y40  r30 w245,one`ntwo`nthree`nfour`nfive
 
Gui, Add, Text,               cblue   x140  y460 R1  , Count:
Gui, Add, Text, vB_Count_GUI  cblue   x180  y460 R1 , %B_Count%  ;add counts for B
 
Gui, add, Edit, vB_Values     cblue   x0 y480 r30 w245, six`nseven`neight`nnine`nten`neleven`ntwelve
gui, font, ;s9, Arial ; Set 10-point Verdana.
 
;**********************A_not_B listview*********************************
Gui, add, Text,                     cred x350  y10   R1 , Count:
Gui, Add, Text, vA_not_B_Count_GUI  cred x400  y10   R1 , %A_Not_B_count% ;count for A_not_B later
;~ guicontrol, hide, A_not_B_Count_GUI
 
Gui, Add, ListView, vA_not_B_LV  cred x250 y22  grid r23 , A Not B                     Control+shift+A  ;-Hdr
for k, v in data[2]
    LV_Add("", v)
 
;**********************B_not_A listview*********************************
Gui, Add, Text,                     cblue x350  y445   R1 , Count:
Gui, Add, Text, vB_not_A_Count_GUI  cblue x400  y445   R1 , %B_Not_A_count% ;count for A_not_B later
 
Gui, Add, ListView, vB_not_A_LV  cblue x250 y460 grid r23, B not A                     Control+shift+B  ; 
for k, v in data[3]
    LV_Add("", v)
 
;**********************A_and_B_Count******************* 
Gui, Add, Text,                     cpurple x600 y110 R1 ,  Count:
Gui, Add, Text, vA_and_B_count_GUI  cpurple x650 y110 R1 , %A_and_B_count% ;Control+shift+c
 
 
Gui, Add, ListView, vA_and_B_LV  cpurple x500 y125 grid r40 , A and B                  Control+shift+c ;-Hdr
for k, v in data[1]
    LV_Add("", v)
;*******************************************************
;**********************buttons*********************************
Gui, Add, Button, x550 y10 Default, Run
Gui, Submit, NoHide
Gui, Show,h900 w750, Compare
return
 
ButtonRun:
Gui, Submit ,NoHide
gosub Crunch_data
Gosub List_Data
return
 
ButtonCancel:
Gui, Destroy
Return
 
;**********************Extract groups*********************************
Crunch_Data:
A_not_B= , B_Not_A= , A_and_B= , A_Count= , B_Count= ;clear variables
D1 := Object(), D2 := Object() ;create empty objects
sort, A_Values, U ;remove duplicates
loop, parse, A_Values, `n, `r ;loop thru rows and covert to objects
	if A_Loopfield !=
        D1[A_index] := A_LoopField
A_Count:=d1.MaxIndex()
 
sort, B_Values, U ;remove duplicates
loop, parse, B_Values, `n, `r ;loop thru rows and covert to objects
	if A_Loopfield !=
        D2[A_index] := A_LoopField
B_Count:=d2.MaxIndex()
;*******************************************************
data:=Compare_Sets(d1,d2) ;call function
 
;**********************breakout returned objects*********************************
for k, v in data[1]
    A_and_B.=v "`r"
 
for k, v in data[2]
    A_not_B.=v "`r"
 
for k, v in data[3]
    B_not_A.=v "`r"
 
;~ clipboard:= B_Not_A
return
 
;***********Put on clipboard******************* 
^+a::
clipboard = ; Empty the clipboard
Clipboard:=A_not_B
return
 
^+b::
clipboard = ; Empty the clipboard
clipboard:= B_Not_A
return
 
^+c::
clipboard = ; Empty the clipboard
clipboard:= A_and_B
return
 
 
;**********************list views*********************************
List_Data:
A_Count:=d1.MaxIndex()
A_Not_B_count:=data[2].MaxIndex()
 
GuiControl, Move, A_Count_GUI       , W100      ;need w incase new text is wider
GuiControl,, A_Count_GUI            , %A_Count% ;updates count
 
GuiControl, Move, A_not_B_Count_GUI , W100 ;need W in case new text is wider
GuiControl,,A_not_B_Count_GUI       , %A_Not_B_count% ;updates count
 
Gui,  ListView, A_not_B_LV  ;      Count:%B_Not_A_count% ;-Hdr    
LV_Delete()
for k, v in data[2]
    LV_Add("", v)
 
 
;***********B_Count********************************************
B_Count:=d2.MaxIndex() ;count for list
B_Not_A_count:=data[3].MaxIndex()
 
 
GuiControl,,B_Not_A_count, %B_Not_A_count% ;updates count
;~ Gui, Add, Text,     cblue x350 y464 R1  , Count: %B_Not_A_count%  ;this overlays on lv. 
 
GuiControl, Move, B_Count_GUI   , W100      ;need W in case new text is wider
GuiControl,,      B_Count_GUI   , %B_Count% ;updates count
 
;***********B_not_A********************************************
Gui,  ListView, B_not_A_LV  ;      Count:%B_Not_A_count% ;-Hdr    
LV_Delete()
for k, v in data[3]
    LV_Add("", v)
 
GuiControl, Move, B_not_A_Count_GUI , W100 ;need W in case new text is wider
GuiControl,     , B_not_A_Count_GUI , %B_Not_A_count% ;updates count
 
 
;**********A_and_B*********************************************
A_and_B_count:=data[1].MaxIndex()
 
GuiControl, Move, A_and_B_count_GUI , W100 ;need W in case new text is wider
GuiControl,     , A_and_B_count_GUI , %A_and_B_Count% ;updates count
 
 
Gui,  ListView,  A_and_B_LV  ;
LV_Delete()
for k, v in data[1]
    LV_Add("", v)
 
Gui, Show,, Compare ;display the gui
return
 
 
 
 
;*******************************************************
GuiEscape:
GuiClose:
ExitApp
return
 
;**********************functions*********************************
Compare_Sets(SetA,SetB){
Temp:=[], Temp2:=[], B_not_A:=[], A_and_B:=[] , A_not_B:=[]
;**********************B not A*********************************    
for i, val in SetA
    Temp[val] := true ;create key with true / false
 
for i, val in SetB ;iterate through set b
	if !Temp[val] ;if the key does not exist
        B_not_A.Insert(val) ;add to B_not_A
 
;**********************A and B*********************************	
for i, val in SetB ;iterate through set b
    if Temp[val] ;if the key does exist
        A_and_B.Insert(val) ;add to A_and_B
 
;**********************A_not_B*********************************
for i, val in SetB
	Temp2[val] := true ;create key with true / false
 
for i, val in SetA ;iterate through set a
	if !Temp2[val] ;if the key does not exist
        A_not_B.Insert(val) ;insert into A_not_B
 
Return [A_and_B, A_not_B, B_Not_A]  ;returns multiple objects
}
 
;**********************Double clicking in A_and_B*********************************
A_and_B_LV:
if A_GuiEvent = DoubleClick
 { RowNumber = 0  ; This causes the first loop iteration to start the search at the top of the list.
Loop
{ RowNumber := LV_GetNext(RowNumber)  ; Resume the search at the row after that found by the previous iteration.
    if not RowNumber  ; The above returned zero, so there are no more selected rows.
        break
 
    LV_GetText(Text, RowNumber,1) ;changed to copy third column 8/25/2013
      AllSelect.=Text . "`n"
    }
 
NewList.=AllSelect . "`n"
AllSelect=
}
 
Clipboard :=NewList
MsgBox,,Clipboard- Number of items: %Count%, % "The following is now on the clipboard`n`n" NewList, 4
NewList=
return