How to express "ancestor" recursively - recursion

I'm stuck with this recursion which doesn't work as I expect.
Where is my mistake?
#!/usr/bin/prolog
% Facts
mother( jeanne , michel ). % great-grandmother, grandfather
mother( genevieve, aubin ). % grandmother, father
mother( irene , alain ). % great-grandmother, grandfather
mother( emilie , colette ). % great-grandmother, grandmother
mother( colette , muriel ). % grandmother, mother
mother( muriel , eve ). % mother, daughter
father( joseph , michel ). % great-grandfather, grandfather
father( michel , aubin ). % grandfather, father
father( xxx , alain ). % great-grandfather, grandfather
father( marcel , colette ). % great-grandfather, grandmother
father( alain , muriel ). % grandfather, mother
father( aubin , eve ). % father, daughter
% Rules
parent( Mother, Child ) :- mother( Mother, Child ).
parent( Father, Child ) :- father( Father, Child ).
ancestors( [Parent|Ancestors], Child ) :-
parent( Parent, Child ),
ancestors( Ancestors, Parent ).
% Queries
:-
ancestors( Ancestor, eve ),
format( 'Eve ancestors: ~w~n', Ancestor ).
% expected answer is [muriel, colette, alain, emilie, marcel, irene, xxx, aubin, michel, genevieve, joseph, jeanne]
EDIT here is the final solution, thank you all.
#!/usr/bin/prolog
/*##- Facts -##*/
mother( jeanne , michel ).
mother( genevieve, sylvie ).
mother( genevieve, brigitte ).
mother( genevieve, aubin ).
mother( irène , alain ).
mother( émilie , colette ).
mother( colette , muriel ).
mother( colette , olivier ).
mother( colette , audrey ).
mother( colette , stéphane ).
mother( muriel , eve ).
father( joseph , michel ).
father( michel , sylvie ).
father( michel , brigitte ).
father( michel , aubin ).
father( séraphin, alain ).
father( marcel , colette ).
father( alain , muriel ).
father( alain , olivier ).
father( yves , audrey ).
father( yves , stéphane ).
father( aubin , eve ).
/*##- Rules -##*/
parent( Mother, Child ) :- mother( Mother, Child ).
parent( Father, Child ) :- father( Father, Child ).
ancestor( Parent, Child ) :- parent( Parent, Child ).
ancestor( GrandParent, Child ) :-
parent( GrandParent, Parent ),
ancestor( Parent, Child ).
grandMothers( GrandMother, Child ) :-
mother( GrandMother, FatherOrMother ),
parent( FatherOrMother, Child ).
grandsFathers( GrandsFather, Child ) :-
father( GrandsFather, FatherOrMother ),
parent( FatherOrMother, Child ).
parents( Mother, Father, Child ) :-
father( Father, Child ),
mother( Mother, Child ).
strictSiblings( SisterOrBrother, Child ) :-
parents( Mother, Father, Child ),
parents( Mother, Father, SisterOrBrother ),
SisterOrBrother \= Child.
siblings( SisterOrBrother, Child ) :-
mother( Mother, Child ), mother( Mother, SisterOrBrother ), SisterOrBrother \= Child ;
father( Father, Child ), father( Father, SisterOrBrother ), SisterOrBrother \= Child .
/*##- Queries -##*/
theMother :-
mother( Mother, eve ),
format( 'Ève\'s mother: ~w~n', [Mother] ).
theFather :-
father( Father, eve ),
format( 'Ève\'s father: ~w~n', [Father] ).
theParents :-
setof( MotherOrFather, parent( MotherOrFather, eve ), MotherAndFather ),
format( 'Ève\'s parents: ~w~n', [MotherAndFather] ).
theGrandMothers :-
setof( GrandMother, grandMothers( GrandMother , eve ), GrandMothers ),
format( 'Ève\'s grand-mothers: ~w~n', [GrandMothers] ).
theGrandFathers :-
setof( GrandsFather, grandsFathers( GrandsFather , eve ), GrandsPères ),
format( 'Ève\'s grand-fathers: ~w~n', [GrandsPères] ).
lesEnfants :-
setof( Child, parents( genevieve, michel, Child ), Children ),
format( 'Geneviève and Michel children: ~w~n', [Children] ).
theTwoParents :-
parents( Mother, Father, eve ),
format( 'Ève\'s mother and father: ~w, ~w~n', [Mother, Father] ).
theStrictSiblings :-
setof( SisterOrBrother, strictSiblings( SisterOrBrother, muriel ), SistersAndBrothers ),
format( 'Muriel\'s strict siblings: ~w~n', [SistersAndBrothers] ).
theSiblings :-
setof( SisterOrBrother, siblings( SisterOrBrother, muriel ), SistersAndBrothers ),
format( 'Muriel\'s siblings: ~w~n', [SistersAndBrothers] ).
theAncestors :-
setof( Ancestor, ancestor( Ancestor, eve ), Ancestors ),
format( 'Ève\'s ancestors: ~w~n', [Ancestors] ).
:-
theMother,
theFather,
theParents,
theGrandMothers,
theGrandFathers,
lesEnfants,
theTwoParents,
theStrictSiblings,
theSiblings,
theAncestors,
halt( 0 ).
And the output is:
Ève's mother: muriel
Ève's father: aubin
Ève's parents: [aubin,muriel]
Ève's grand-mothers: [colette,genevieve]
Ève's grand-fathers: [alain,michel]
Geneviève and Michel children: [aubin,brigitte,sylvie]
Ève's mother and father: muriel, aubin
Muriel's strict siblings: [olivier]
Muriel's siblings: [audrey,olivier,stéphane]
Ève's ancestors: [alain,aubin,colette,genevieve,irène,jeanne,joseph,marcel,michel,muriel,séraphin,émilie]

Let's do this interactively (in SWI Prolog) instead of in a script which prints the answers at the end using format/2.
We want all possible ancestors of eve in a list.
So we have to
query the Prolog program for all possible solutions to the goal ancestor(A,eve)
and then collect them into a list
This is done using one of the predicates bagof/3, setof/3 or findall/3, which backtrack over answers to a goal and unify a variable with a list containing all the answers (with duplicate answers for bagof/3, without duplicate answers for setof/3, and with "no possible answer" yielding [] instead of failure for findall/3).
So we just need to make sure the goal to find any ancestor is correct.
We can state that A is an ancestor of C if
A is a parent of C or
A is a parent of some D, and D is an ancestor of C
(Note: just 'if', not 'if an only if'. However, it is assumed there are no other ways in which A could possibly be an ancestor of C ... a reasonable "closed world assumption")
The above formulation is well adapted to the search strategy of Prolog, which attempts to resolve a leftmost sub-goal in the body first:
ancestor(A,C) :- parent(A,C).
ancestor(A,C) :- parent(A,D),ancestor(D,C).
Doing it in the way "check for ancestor on the left":
ancestor(A,C) :- parent(A,C).
ancestor(A,C) :- ancestor(A,D),parent(D,C).
should lead to the same result but actually does not: After initially giving good answer, the Prolog Processor will eventually enter an infinite loop, where ancestor(A,C) calls ancestor(A,D). (This would work in the simpler "Datalog" language).
Anyway, are we done?
?- ancestor(X,eve).
X = muriel ;
X = aubin ;
X = jeanne ;
X = genevieve ;
X = irene ;
X = emilie ;
X = colette ;
X = joseph ;
X = michel ;
X = xxx ;
X = marcel ;
X = alain ;
false.
Now collect everything into a list:
(In SWI-Prolog, you have to say that you want long lists printed, not replaced by ellipses, so):
?- set_prolog_flag(answer_write_options,[max_depth(0)]).
true.
And then:
?- bagof(X,ancestor(X,eve),Out).
Out = [muriel,aubin,jeanne,genevieve,irene,emilie,
colette,joseph,michel,xxx,marcel,alain].

Related

List all name pairs that appear in a line together and count their frequency

I have the following file (2016.csv the head of the file is presented below)
Zhichen Gong,Huanhuan Chen
Zhichuan Huang,Tiantian Xie,Ting Zhu,Jianwu Wang,Qingquan Zhang
Zhichuan Huang,Ting Zhu
Zhifei Zhang,Yang Song,Wei Wang 0063,Hairong Qi
I am using the following awk loop to find all possible pairs of names that appear together in a line of the above file together.
awk -F, '{for(i=1;i<NF;i++){for(j=i+1;j<=NF;j++){if($i > $j){k[$i][$j]}else{k[$j][$i]}}}}END{for(n in k){for (l in k[n]){print n,",",l}}}' 2016.csv
The output of this awk loop is the following :
Zhichen Gong , Huanhuan Chen
Zhichuan Huang , Tiantian Xie
Zhichuan Huang , Ting Zhu
Zhichuan Huang , Jianwu Wang
Zhichuan Huang , Qingquan Zhang
Zhifei Zhang,Yang Song
Zhifei Zhang,Wei Wang 0063
Zhifei Zhang,Hairong Qi
etc
This loop works fine and finds all the pairs that appear in a line of the initial file together. The only thing I want to add is a counter next to each line of the awk output, which will show how many times this pair exists in the initial file.
For example, for the above awk output, I want it to be like:
Zhichen Gong , Huanhuan Chen, 1
Zhichuan Huang , Tiantian Xie, 1
Zhichuan Huang , Ting Zhu, 2
Zhichuan Huang , Jianwu Wang, 1
Zhichuan Huang , Qingquan Zhang, 1
Zhifei Zhang,Yang Song, 1
Zhifei Zhang,Wei Wang 0063,1
Zhifei Zhang,Hairong Qi,1
Where 1 in the first line (Zhichen Gong , Huanhuan Chen, 1) shows that this pair of names exists 1 time in the initial file.
I assume that I just have to add a counter in the awk loop, but I couldn't do it up to now properly.
Using OP's 11-line sample as our input:
$ cat 2016.csv
Zhichen Gong,Huanhuan Chen
Zhichuan Huang,Tiantian Xie,Ting Zhu,Jianwu Wang,Qingquan Zhang
Zhichuan Huang,Ting Zhu
Zhifei Zhang,Yang Song,Wei Wang 0063,Hairong Qi
Zhihao Huang,Hui Li,Xin Li,Wei He
Zhijun Yin,You Chen,Daniel Fabbri,Jimeng Sun,Bradley A. Malin
Zhipeng Huang 0001,Bogdan Cautis,Reynold Cheng,Yudian Zheng
Zhipeng Huang 0001,Yudian Zheng,Reynold Cheng,Yizhou Sun,Nikos Mamoulis,Xiang Li 0067
Zhiqiang Tao,Hongfu Liu,Sheng Li 0001,Yun Fu 0001
Zhiqiang Xu,Yiping Ke
Zhiyuan Chen 0001,Estevam R. Hruschka Jr.,Bing Liu 0001
Making a few tweaks to OP's current code to keep track of counts and then ordering the output 1st by count and then names:
awk '
BEGIN { FS=","; OFS=" , " }
{ for (i=1;i<NF;i++)
for(j=i+1;j<=NF;j++)
if ($i > $j) k[$i][$j]++ # increment counter
else k[$j][$i]++ # increment counter
}
END { # to sort by count we will create a new 3-dimensional array with the count as the 1st dimension
for (i in k)
for (j in k[i]) {
arr[k[i][j]][i][j] # arr[count][i][j]
delete k[i][j] # delete old array entry to limit memory usage
}
PROCINFO["sorted_in"]="#ind_num_desc" # sort 1st index by count/descending
for (cnt in arr) {
PROCINFO["sorted_in"]="#ind_str_asc" # sort 2nd/3rd indices by name/ascending
for (i in arr[cnt])
for (j in arr[cnt][i])
print i,j,cnt
}
}
' 2016.csv
NOTES:
assumes we have enough memory for the 3-dimensional array; then again ...
memory usage for these 2-/3-dimensional arrays should be significantly smaller than the other answers that utilize a compound index for a 1-dimensional array, ie, ...
[bob][smith] and [bob][jones] will require bob to be stored once in memory while [bob,smith] and [bob,jones] will require bob to be stored twice in memory
OP's expected output has a mix of output delimiters; the use of OFS=" , " matches the earlier edits by OP; OP can modify OFS as needed
This generates the following 61-line output:
Yudian Zheng , Reynold Cheng , 2
Zhichuan Huang , Ting Zhu , 2
Zhipeng Huang 0001 , Reynold Cheng , 2
Zhipeng Huang 0001 , Yudian Zheng , 2
Daniel Fabbri , Bradley A. Malin , 1
Estevam R. Hruschka Jr. , Bing Liu 0001 , 1
Jimeng Sun , Bradley A. Malin , 1
Jimeng Sun , Daniel Fabbri , 1
Qingquan Zhang , Jianwu Wang , 1
Reynold Cheng , Bogdan Cautis , 1
Reynold Cheng , Nikos Mamoulis , 1
Sheng Li 0001 , Hongfu Liu , 1
Tiantian Xie , Jianwu Wang , 1
Tiantian Xie , Qingquan Zhang , 1
Ting Zhu , Jianwu Wang , 1
Ting Zhu , Qingquan Zhang , 1
Ting Zhu , Tiantian Xie , 1
Wei He , Hui Li , 1
Wei Wang 0063 , Hairong Qi , 1
Xiang Li 0067 , Nikos Mamoulis , 1
Xiang Li 0067 , Reynold Cheng , 1
Xin Li , Hui Li , 1
Xin Li , Wei He , 1
Yang Song , Hairong Qi , 1
Yang Song , Wei Wang 0063 , 1
Yizhou Sun , Nikos Mamoulis , 1
Yizhou Sun , Reynold Cheng , 1
Yizhou Sun , Xiang Li 0067 , 1
You Chen , Bradley A. Malin , 1
You Chen , Daniel Fabbri , 1
You Chen , Jimeng Sun , 1
Yudian Zheng , Bogdan Cautis , 1
Yudian Zheng , Nikos Mamoulis , 1
Yudian Zheng , Xiang Li 0067 , 1
Yudian Zheng , Yizhou Sun , 1
Yun Fu 0001 , Hongfu Liu , 1
Yun Fu 0001 , Sheng Li 0001 , 1
Zhichen Gong , Huanhuan Chen , 1
Zhichuan Huang , Jianwu Wang , 1
Zhichuan Huang , Qingquan Zhang , 1
Zhichuan Huang , Tiantian Xie , 1
Zhifei Zhang , Hairong Qi , 1
Zhifei Zhang , Wei Wang 0063 , 1
Zhifei Zhang , Yang Song , 1
Zhihao Huang , Hui Li , 1
Zhihao Huang , Wei He , 1
Zhihao Huang , Xin Li , 1
Zhijun Yin , Bradley A. Malin , 1
Zhijun Yin , Daniel Fabbri , 1
Zhijun Yin , Jimeng Sun , 1
Zhijun Yin , You Chen , 1
Zhipeng Huang 0001 , Bogdan Cautis , 1
Zhipeng Huang 0001 , Nikos Mamoulis , 1
Zhipeng Huang 0001 , Xiang Li 0067 , 1
Zhipeng Huang 0001 , Yizhou Sun , 1
Zhiqiang Tao , Hongfu Liu , 1
Zhiqiang Tao , Sheng Li 0001 , 1
Zhiqiang Tao , Yun Fu 0001 , 1
Zhiqiang Xu , Yiping Ke , 1
Zhiyuan Chen 0001 , Bing Liu 0001 , 1
Zhiyuan Chen 0001 , Estevam R. Hruschka Jr. , 1
If the order of the output does not matter the END{...} block can be simplified to the following:
END { for (i in k)
for (j in k[i])
print i,j,k[i][j]
}
find all possible pairs of names that appear together their counts
You may use this awk solution:
awk -F, -v OFS=" , " '
{
for (i=1; i<NF; i++)
++fq[$i OFS $(i+1)]
}
END {
for (i in fq) print i, fq[i]
}' file
Using a sensible sample input file so we can tell at a glance if the script worked or not because the expected output is obvious:
$ cat file
a,b,c
c,a
e,d
This will do what you want using any awk:
$ cat tst.awk
BEGIN { FS=OFS="," }
{
for (i=1; i<NF; i++) {
for (j=i+1; j<=NF; j++) {
cnt[( $i < $j ? $i FS $j : $j FS $i )]++
}
}
}
END {
for ( pair in cnt ) {
print pair, cnt[pair]
}
}
$ awk -f tst.awk file
a,b,1
a,c,2
d,e,1
b,c,1
or if you want it sorted:
$ awk -f tst.awk file | sort
a,b,1
a,c,2
b,c,1
d,e,1
With the OPs provided sample input:
$ cat file2
Zhichen Gong,Huanhuan Chen
Zhichuan Huang,Tiantian Xie,Ting Zhu,Jianwu Wang,Qingquan Zhang
Zhichuan Huang,Ting Zhu
Zhifei Zhang,Yang Song,Wei Wang 0063,Hairong Qi
we have:
$ awk -f tst.awk file2 | sort
Hairong Qi,Wei Wang 0063,1
Hairong Qi,Yang Song,1
Hairong Qi,Zhifei Zhang,1
Huanhuan Chen,Zhichen Gong,1
Jianwu Wang,Qingquan Zhang,1
Jianwu Wang,Tiantian Xie,1
Jianwu Wang,Ting Zhu,1
Jianwu Wang,Zhichuan Huang,1
Qingquan Zhang,Tiantian Xie,1
Qingquan Zhang,Ting Zhu,1
Qingquan Zhang,Zhichuan Huang,1
Tiantian Xie,Ting Zhu,1
Tiantian Xie,Zhichuan Huang,1
Ting Zhu,Zhichuan Huang,2
Wei Wang 0063,Yang Song,1
Wei Wang 0063,Zhifei Zhang,1
Yang Song,Zhifei Zhang,1

Understanding Eliza Chatbot in prolog

I'm trying to understand how it really works the next eliza chatbot provided as example at:
https://swish.swi-prolog.org/p/yet%20another%20eliza.pl
What I so far understand is, that at the beginning we have a list (Dictionary) empty that will be fulfill with the matches of each word and each integer.** The matching of words and integers will be performed through
match([N|Pattern], Dictionary, Target) :-
integer(N), lookup(N, Dictionary, LeftTarget),
append(LeftTarget, RightTarget, Target),
match(Pattern, Dictionary, RightTarget).
match([Word | Pattern], Dictionary, [Word | Target]) :-
atom(Word), match(Pattern, Dictionary, Target).
match([], _Dictionary, []).
The matches will be saved in the dictionary through:
lookup(Key, [(Key, Value) | _Dict], Value).
lookup(Key, [(Key1, _Val1) | Dictionary], Value) :-
Key \= Key1, lookup(Key, Dictionary, Value).
I have been also reading the explanation found in the book "The art of prolog" of Leon Sterling and Ehud Shapiro but I can't really get it
I would appreciate your help
eliza :-
write('? '), read_word_list(Input), eliza(Input), !.
eliza([bye]) :-
write('Goodbye. I hope I have helped you'), nl.
eliza(Input) :-
pattern(Stimulus, Response),
match(Stimulus, Dictionary, Input),
match(Response, Dictionary, Output),
reply(Output),
!, eliza.
match([N|Pattern], Dictionary, Target) :-
integer(N), lookup(N, Dictionary, LeftTarget),
append(LeftTarget, RightTarget, Target),
match(Pattern, Dictionary, RightTarget).
match([Word | Pattern], Dictionary, [Word | Target]) :-
atom(Word), match(Pattern, Dictionary, Target).
match([], _Dictionary, []).
pattern([i,am,1],[how,long,have,you,been,1,'?']).
pattern([1,you,2,me],[what,makes,you,think,i,2,you,'?']).
pattern([i,like,1],[does,anyone,else,in,your,family,like,1,'?']).
pattern([i,feel,1],[do,you,often,feel,that,way,'?']).
pattern([1,X,2],[can,you,tell,me,more,about,your,X,'?']) :- important(X).
pattern([1],[please,go,on]).
important(father).
important(mother).
important(son).
important(sister).
important(brother).
important(daughter).
reply([Head | Tail]) :-
write(Head), write(' '), reply(Tail).
reply([]) :- nl.
lookup(Key, [(Key, Value) | _Dict], Value).
lookup(Key, [(Key1, _Val1) | Dictionary], Value) :-
Key \= Key1, lookup(Key, Dictionary, Value).
read_word_list(Ws) :-
read(A),
tokenize_atom(A, Ws).

SPARQL count unique value combinations

I have been working on a SPARQL query to find unique value combinations in my graph store. But I dont succeed.
Basically what I try to do is:
a b c
e f g
e r t
a b c
k l m
e f g
a b c
result:
a b c | 3
e f g | 2
e r t | 1
k l m | 1
Tried several constructions, with distincts, group by`s and sub queries but I dont succeed.
Last Try:
SELECT (count (*) as ?n){
SELECT DISTINCT ?value1 ?value2 ?value3 WHERE {
?instance vocab:relate ?value1 .
?instance vocab:relate ?value2 .
?instance vocab:relate ?value3 .
}
}
RDF:
<http://test.example.com/instance1>
a <http://test.example.com#Instance> ;
<http://vocab.example.com/relate>
<http://test.example.com/c> , <http://test.example.com/b> , <http://test.example.com/a> .
<http://test.example.com/instance6>
a <http://test.example.com#Instance> ;
<http://vocab.example.com/relate>
<http://test.example.com/g> , <http://test.example.com/f> , <http://test.example.com/e> .
<http://test.example.com/instance4>
a <http://test.example.com#Instance> ;
<http://vocab.example.com/relate>
<http://test.example.com/c> , <http://test.example.com/b> , <http://test.example.com/a> .
<http://test.example.com/instance2>
a <http://test.example.com#Instance> ;
<http://vocab.example.com/relate>
<http://test.example.com/g> , <http://test.example.com/f> , <http://test.example.com/e> .
<http://test.example.com/instance7>
a <http://test.example.com#Instance> ;
<http://vocab.example.com/relate>
<http://test.example.com/c> , <http://test.example.com/b> , <http://test.example.com/a> .
<http://test.example.com/instance5>
a <http://test.example.com#Instance> ;
<http://vocab.example.com/relate>
<http://test.example.com/m> , <http://test.example.com/l> , <http://test.example.com/k> .
<http://test.example.com/instance3>
a <http://test.example.com#Instance> ;
<http://vocab.example.com/relate>
<http://test.example.com/t> , <http://test.example.com/r> , <http://test.example.com/e> .
AKSW's comment is spot on: you need to add an ordering criteria to the values so that you're not considering all the different possible ways of ordering the values. Also, remember that RDF doesn't have "duplicate" triples, so
:a :p :c, :c, :d
is the same as
:a :p :c, :d
so the appropriate comparison is < as opposed to <=, since without duplicate triples, you'd never have an = case. Also, since the values are IRIs, you need to get their string values before you can compare with <, but the str function will take care of that.
prefix v: <http://vocab.example.com/>
prefix : <http://test.example.com/>
select ?a ?b ?c (count(distinct ?i) as ?count) where {
?i v:relate ?a, ?b, ?c .
filter (str(?a) < str(?b) && str(?b) < str(?c))
}
group by ?a ?b ?c
------------------------
| a | b | c | count |
========================
| :a | :b | :c | 3 |
| :e | :f | :g | 2 |
| :e | :r | :t | 1 |
| :k | :l | :m | 1 |
------------------------

how to remove all characters except for those enclosed by parentheses

Suppose you have a text file like this below
I have apples, bananas, ( some pineapples over 4 ), and cherries ( coconuts with happy face :D ) and so on. You may help yourself except for cherries ( they are for my parents sorry ;C ) . I feel like I can run a fruit business.
What I aim to do is to erase all characters except for those enclosed by parentheses. Please keep in mind that the characters in a pair of parentheses can be varied ranging from English to other characters but no other punctuations may play a role as enclosing characters: only parentheses can be allowed to do.
I think I should utilize gsub but not sure.
This is what I want to have as a result.
( some pineapples over 4 ) ( coconuts with happy face :D ) ( they are for my parents sorry ;C )
Whether using a way of removing or extracting, I hope to get the result above.
We can do this by extracting the substring within the brackets and paste it together
library(stringr)
paste(str_extract_all(str1, "\\([^)]*\\)")[[1]], collapse=' ')
#[1] "( some pineapples over 4 ) ( coconuts with happy face :D ) ( they are for my parents sorry ;C )"
Or we can use a gsub based solution
trimws(gsub("\\s+\\([^)]*\\)(*SKIP)(*FAIL)|.", "", str1, perl = TRUE))
#[1] "( some pineapples over 4 ) ( coconuts with happy face :D ) ( they are for my parents sorry ;C )"
data
str1 <- "I have apples, bananas, ( some pineapples over 4 ), and cherries ( coconuts with happy face :D ) and so on. You may help yourself except for cherries ( they are for my parents sorry ;C ) . I feel like I can run a fruit business."

Using Macros to generate graph parameters SAS

I am attempting to automate a graph process using SAS macros. Since this will be used for several different subsets, the axes of the graph must be adjusted accordingly. I haver tried a few different ways and feel that I'm going the wrong way down the rabbit hole.
Here is my dataset.
data want;
input A B C D;
cards;
100 5 6 1
200 5 5 2
150 5.5 5.5 3
457 4.2 6.2 4
500 3.7 7.0 5
525 3.5 7.2 6
;
run;
What I want is a graph that has the following axis specs:
x-axis from min(D) to max(D) by some reasonable increment
left-axis from min(A) to max(A)
right-axis from min (B,C) to max(B,C)
Here is my latest attempt:
proc sql;
select roundz((max(A)+100), 100),
roundz(min(A), 100),
(&maxA.-&minA.)/10,
roundz(max(B, C)+1, 1),
roundz(min(B, C), 1),
(&maxBC.-&minBC.)/10,
roundz(max(D), 1),
roundz(min(D), 1),
(&maxD.-&minD.+1)/3
into :maxA, :minA, :Ainc,
:maxBC, :minBC, :BCinc,
:maxD, :minD, :Dinc
from want;
run;
goptions reset=all ftext=SWISS htext=2.5 ;
axis1 order=(&minA to &maxA by &Ainc) minor=none label=(angle=90 'A label' ) offset=(1) ;
axis2 order=(&minBC to &maxBC by &BCinc) minor=(number=1) label=(angle=90 'BC Label') offset=(1);
axis3 order=(&minD to &maxD by &Dinc) minor=(number=2) label=('D') offset=(1) ;
symbol1 color=black i=join value=circle height=2 width=2 ;
symbol2 color=black i=join value=square height=2 width=2 ;
symbol3 color=black i=join value=triangle height=2 width=2 ;
legend1 label=none mode=reserve position=(top center outside) value=('Label here' ) shape=symbol(5,1) ;
legend2 label=none mode=reserve position=(top center outside) value=('label 1' 'label 2') shape=symbol(3,1) ;
proc gplot data=want;
plot A*D=1 /overlay legend=legend1 vaxis=axis1 haxis=axis3 ;
plot2 B*D=2 &var_C*D=3 /overlay legend=legend2 vaxis=axis2 ;
run ;
Any help would be greatly appreciated. Even if that means a completely different way of doing it (though I'd also be interested to see where I am going wrong here).
Thanks, Pyll
What you're doing is sort-of writing a macro without writing a macro. Write the macro and this is easier. Also, if you're going to have the INCs always be 1/10ths, put that in let statements (although if they might vary in their conception, then leave them as parameters).
%macro graph_me(minA=,maxA=, minBC=,maxBC=, minD=, maxD=);
%let incA = %sysevalf((&maxA.-&minA.)/10); *same for incD and incBC;
goptions reset=all ftext=SWISS htext=2.5 ;
axis1 order=(&minA to &maxA by &incA) minor=none label=(angle=90 'A label' ) offset=(1) ;
axis2 order=(&minBC to &maxBC by &incBC) minor=(number=1) label=(angle=90 'BC Label') offset=(1);
axis3 order=(&minD to &maxD by &incD) minor=(number=2) label=('D') offset=(1) ;
symbol1 color=black i=join value=circle height=2 width=2 ;
symbol2 color=black i=join value=square height=2 width=2 ;
symbol3 color=black i=join value=triangle height=2 width=2 ;
legend1 label=none mode=reserve position=(top center outside) value=('Label here' ) shape=symbol(5,1) ;
legend2 label=none mode=reserve position=(top center outside) value=('label 1' 'label 2') shape=symbol(3,1) ;
%mend graph_me;
Now write your SQL call to grab those parameters into the macro call itself.
proc sql NOPRINT;
select
cats('%graph_me(minA=',roundz(min(A), 100),
',maxA=', roundz((max(A)+100), 100),
... etc. ...
into :mcall
from want;
quit;
This gives you the advantage that you may be able to generate multiple calls if you, for example, want to do this grouped by some variable (having one graph per variable value).
2 things in the sql:
you cannot use the macros you are creating and you need just one value, when doing max(B,C) you are creating as many values as there are obs in the dataset, you need another max.
I cannot check the sas graph part as I do not have it, but
proc sql NOPRINT;
select roundz((max(A)+100), 100) as maxA,
roundz(min(A), 100) as minA,
((calculated maxA)-(calculated minA))/10,
roundz(max(max(B, C))+1, 1) as maxBC,
roundz(min(min(B, C)), 1) as minBC,
((calculated maxBC)-(calculated minBC))/10,
roundz(max(D), 1) as maxD,
roundz(min(D), 1) as minD,
((calculated maxD)-(calculated minD)+1)/3
into :maxA, :minA, :Ainc,
:maxBC, :minBC, :BCinc,
:maxD, :minD, :Dinc
from want;
quit;

Resources