Skip to content

Commit 1f93637

Browse files
authored
More detailed reporting of the rule parsing errors.
1 parent 390c32d commit 1f93637

File tree

3 files changed

+249
-40
lines changed

3 files changed

+249
-40
lines changed

adaa.analytics.rules/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ codeQuality {
2727
}
2828

2929
sourceCompatibility = 1.8
30-
version = '1.7.5'
30+
version = '1.7.6'
3131

3232

3333
jar {

adaa.analytics.rules/src/main/java/adaa/analytics/rules/logic/representation/RuleParser.java

Lines changed: 76 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616

1717
import com.rapidminer.operator.ports.metadata.AttributeMetaData;
1818
import com.rapidminer.operator.ports.metadata.ExampleSetMetaData;
19+
import org.apache.commons.lang.math.NumberUtils;
1920

2021
import java.util.ArrayList;
2122
import java.util.List;
23+
import java.util.logging.Level;
2224
import java.util.regex.Matcher;
2325
import java.util.regex.Pattern;
2426

@@ -39,45 +41,50 @@ public class RuleParser {
3941
*/
4042
public static Rule parseRule(String s, ExampleSetMetaData meta) {
4143
Rule rule = null;
42-
43-
Pattern pattern = Pattern.compile("IF\\s+(?<premise>.+)\\s+THEN(?<consequence>\\s+.*|\\s*)");
44-
Matcher matcher = pattern.matcher(s);
45-
46-
boolean isSurvival = false;
44+
45+
Pattern pattern = Pattern.compile("IF\\s+(?<premise>.+)\\s+THEN(?<consequence>\\s+.*|\\s*)");
46+
Matcher matcher = pattern.matcher(s);
47+
48+
boolean isSurvival = false;
4749
if (meta.getAttributeByRole(SurvivalRule.SURVIVAL_TIME_ROLE) != null) {
4850
isSurvival = true;
4951
}
50-
51-
if (matcher.find()) {
52-
String pre = matcher.group("premise");
53-
String con = matcher.group("consequence");
54-
55-
ElementaryCondition consequence;
56-
CompoundCondition premise = parseCompoundCondition(pre, meta);
57-
58-
if (con == null || con.trim().length() == 0) {
59-
if (!meta.getLabelMetaData().isNumerical())
60-
throw new IllegalArgumentException("Empty conclusion for non-numeric label attribute");
61-
consequence = new ElementaryCondition();
62-
consequence.attribute = meta.getLabelMetaData().getName();
63-
consequence.valueSet = new SingletonSet(NaN, null);
64-
consequence.adjustable = false;
65-
consequence.disabled = false;
52+
53+
if (matcher.find()) {
54+
String pre = matcher.group("premise");
55+
String con = matcher.group("consequence");
56+
57+
ElementaryCondition consequence = null;
58+
CompoundCondition premise = parseCompoundCondition(pre, meta);
59+
60+
if (con == null || con.trim().length() == 0) {
61+
if (!meta.getLabelMetaData().isNumerical()) {
62+
Logger.log("Empty conclusion for nominal label"+ "\n", Level.WARNING);
63+
} else {
64+
consequence = new ElementaryCondition();
65+
consequence.attribute = meta.getLabelMetaData().getName();
66+
consequence.valueSet = new SingletonSet(NaN, null);
67+
consequence.adjustable = false;
68+
consequence.disabled = false;
69+
}
6670
} else {
6771
consequence = parseElementaryCondition(con, meta);
6872
}
69-
70-
if (premise == null || consequence == null) {
71-
return null;
72-
}
73-
74-
rule = meta.getLabelMetaData().isNominal()
75-
? new ClassificationRule(premise, consequence)
76-
: (isSurvival
73+
74+
if (premise != null && consequence != null) {
75+
76+
rule = meta.getLabelMetaData().isNominal()
77+
? new ClassificationRule(premise, consequence)
78+
: (isSurvival
7779
? new SurvivalRule(premise, consequence)
7880
: new RegressionRule(premise, consequence));
79-
}
80-
81+
}
82+
}
83+
84+
if (rule == null) {
85+
Logger.log("Omitting expert's knowledge entry: " + s + "\n", Level.WARNING);
86+
}
87+
8188
return rule;
8289
}
8390

@@ -143,6 +150,10 @@ public static ElementaryCondition parseElementaryCondition(String s, ExampleSetM
143150
IValueSet valueSet = null;
144151

145152
AttributeMetaData attributeMeta = meta.getAttributeByName(attribute);
153+
if (attributeMeta == null) {
154+
Logger.log("Attribute <" + attribute + "> not found"+ "\n", Level.WARNING);
155+
return null;
156+
}
146157

147158
ConditionBase.Type type = (numBrackets == 0) ? ConditionBase.Type.NORMAL :
148159
((numBrackets == 1) ? ConditionBase.Type.PREFERRED : ConditionBase.Type.FORCED);
@@ -159,7 +170,8 @@ public static ElementaryCondition parseElementaryCondition(String s, ExampleSetM
159170
mapping.addAll(attributeMeta.getValueSet());
160171
double v = mapping.indexOf(value);
161172
if (v == -1) {
162-
return null;
173+
Logger.log("Invalid value <" + value + "> of the nominal attribute <" + attribute + ">"+ "\n", Level.WARNING);
174+
return null;
163175
}
164176
valueSet = new SingletonSet(v, mapping);
165177

@@ -180,14 +192,36 @@ public static ElementaryCondition parseElementaryCondition(String s, ExampleSetM
180192
matcher = regex.matcher(valueString);
181193

182194
if (matcher.find()) {
195+
183196
String lo = matcher.group("lo");
184197
String hi = matcher.group("hi");
185-
186-
valueSet = new Interval(
187-
lo.equals("-inf") ? Interval.MINUS_INF : Double.parseDouble(lo),
188-
hi.equals("inf")? Interval.INF : Double.parseDouble(hi),
189-
leftClosed, rightClosed);
190-
}
198+
199+
double numLo = Double.NaN;
200+
double numHi = Double.NaN;
201+
202+
if (lo.equals("-inf")) {
203+
numLo = Interval.MINUS_INF;
204+
} else if (NumberUtils.isNumber(lo)) {
205+
numLo = Double.parseDouble(lo);
206+
} else {
207+
Logger.log("Invalid lower interval bound: " + lo + "\n" , Level.WARNING);
208+
return null;
209+
}
210+
211+
if (hi.equals("inf")) {
212+
numHi = Interval.INF;
213+
} else if (NumberUtils.isNumber(hi)) {
214+
numHi = Double.parseDouble(hi);
215+
} else {
216+
Logger.log("Invalid upper interval bound: " + hi + "\n", Level.WARNING );
217+
return null;
218+
}
219+
220+
valueSet = new Interval(numLo, numHi, leftClosed, rightClosed);
221+
} else {
222+
Logger.log("Invalid interval: " + valueString, Level.WARNING );
223+
return null;
224+
}
191225
}
192226
}
193227

@@ -196,8 +230,11 @@ public static ElementaryCondition parseElementaryCondition(String s, ExampleSetM
196230
out.setType(type);
197231
out.setAdjustable(adjustable);
198232
}
199-
}
233+
} else {
234+
Logger.log("Invalid elementary condition: " + s + "\n", Level.WARNING );
235+
}
200236

201237
return out;
202238
}
239+
203240
}

examples/guider-errors.xml

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
<?xml version="1.0"?>
2+
<experiment>
3+
<parameter_sets>
4+
5+
<!--=======================================================================================================================-->
6+
7+
<parameter_set name="illegal attribute">
8+
<param name="use_expert">true</param>
9+
<param name ="expert_rules">
10+
<entry name="rule-0">IF [[gimpul = (-inf, 750)]] THEN class = {0}</entry>
11+
</param>
12+
<param name ="expert_preferred_conditions">
13+
</param>
14+
<param name ="expert_forbidden_conditions">
15+
</param>
16+
</parameter_set>
17+
18+
<!--=======================================================================================================================-->
19+
20+
<parameter_set name="illegal_class_value">
21+
<param name="use_expert">true</param>
22+
<param name ="expert_rules">
23+
</param>
24+
<param name ="expert_preferred_conditions">
25+
<entry name="preferred-attribute-0">1: IF [[gimpuls = Any]] THEN class = {14}</entry>
26+
</param>
27+
<param name ="expert_forbidden_conditions">
28+
</param>
29+
</parameter_set>
30+
31+
<!--=======================================================================================================================-->
32+
33+
<parameter_set name="illegal_attribute_value">
34+
<param name="use_expert">true</param>
35+
<param name ="expert_rules">
36+
</param>
37+
<param name ="expert_preferred_conditions">
38+
<entry name="preferred-condition-01">inf: IF [[seismic = {q}]] THEN class = {0}</entry>
39+
</param>
40+
<param name ="expert_forbidden_conditions">
41+
</param>
42+
</parameter_set>
43+
44+
<!--=======================================================================================================================-->
45+
46+
<parameter_set name="illegal_equality">
47+
<param name="use_expert">true</param>
48+
<param name ="expert_rules">
49+
</param>
50+
<param name ="expert_preferred_conditions">
51+
<entry name="preferred-condition-01">inf: IF [[seismic - {q}]] THEN class = {0}</entry>
52+
</param>
53+
<param name ="expert_forbidden_conditions">
54+
</param>
55+
</parameter_set>
56+
57+
<!--=======================================================================================================================-->
58+
59+
<parameter_set name="illegal_if_then">
60+
<param name="use_expert">true</param>
61+
<param name ="expert_rules">
62+
</param>
63+
<param name ="expert_preferred_conditions">
64+
<entry name="preferred-condition-01">inf: IFF [[seismic = {a}]] THEN class = {0}</entry>
65+
</param>
66+
<param name ="expert_forbidden_conditions">
67+
</param>
68+
</parameter_set>
69+
70+
<!--=======================================================================================================================-->
71+
72+
<parameter_set name="illegal_interval_comma">
73+
<param name="use_expert">true</param>
74+
<param name ="expert_rules">
75+
<entry name="rule-0">IF [[gimpuls = (-inf 750)]] THEN class = {0}</entry>
76+
</param>
77+
<param name ="expert_preferred_conditions">
78+
</param>
79+
<param name ="expert_forbidden_conditions">
80+
</param>
81+
</parameter_set>
82+
83+
<!--=======================================================================================================================-->
84+
85+
<parameter_set name="illegal_interval_no_left">
86+
<param name="use_expert">true</param>
87+
<param name ="expert_rules">
88+
<entry name="rule-0">IF [[gimpuls = -inf, 750)]] THEN class = {0}</entry>
89+
</param>
90+
<param name ="expert_preferred_conditions">
91+
</param>
92+
<param name ="expert_forbidden_conditions">
93+
</param>
94+
</parameter_set>
95+
96+
<!--=======================================================================================================================-->
97+
98+
<parameter_set name="illegal_interval_no_right">
99+
<param name="use_expert">true</param>
100+
<param name ="expert_rules">
101+
<entry name="rule-0">IF [[gimpuls = (-inf, 750]] THEN class = {0}</entry>
102+
</param>
103+
<param name ="expert_preferred_conditions">
104+
</param>
105+
<param name ="expert_forbidden_conditions">
106+
</param>
107+
</parameter_set>
108+
109+
<!--=======================================================================================================================-->
110+
111+
<parameter_set name="illegal_lowerbound">
112+
<param name="use_expert">true</param>
113+
<param name ="expert_rules">
114+
<entry name="rule-0">IF [[gimpuls = (-inff, 750)]] THEN class = {0}</entry>
115+
</param>
116+
<param name ="expert_preferred_conditions">
117+
</param>
118+
<param name ="expert_forbidden_conditions">
119+
</param>
120+
</parameter_set>
121+
122+
<!--=======================================================================================================================-->
123+
124+
<parameter_set name="illegal_upperbound">
125+
<param name="use_expert">true</param>
126+
<param name ="expert_rules">
127+
<entry name="rule-0">IF [[gimpuls = (-inf, a750)]] THEN class = {0}</entry>
128+
</param>
129+
<param name ="expert_preferred_conditions">
130+
</param>
131+
<param name ="expert_forbidden_conditions">
132+
</param>
133+
</parameter_set>
134+
135+
<!--=======================================================================================================================-->
136+
137+
<parameter_set name="illegal_upperbound_2">
138+
<param name="use_expert">true</param>
139+
<param name ="expert_rules">
140+
<entry name="rule-0">IF [[gimpuls = (-inf, 0.5.4)]] THEN class = {0}</entry>
141+
</param>
142+
<param name ="expert_preferred_conditions">
143+
</param>
144+
<param name ="expert_forbidden_conditions">
145+
</param>
146+
</parameter_set>
147+
148+
</parameter_sets>
149+
150+
<datasets>
151+
<dataset>
152+
<label>class</label>
153+
<out_directory>./results-guider/errors</out_directory>
154+
155+
<training>
156+
<report_file>training.txt</report_file>
157+
158+
<train>
159+
<in_file>../data/seismic-bumps/seismic-bumps.arff</in_file>
160+
<model_file>seismic-bumps-full.mdl</model_file>
161+
</train>
162+
163+
</training>
164+
165+
<prediction>
166+
167+
</prediction>
168+
169+
170+
</dataset>
171+
</datasets>
172+
</experiment>

0 commit comments

Comments
 (0)