qdfca 1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,58 @@
1
+ = Quick-Deploy Formal Concept Analysis
2
+
3
+ Version:: 1.0
4
+ Author:: David Flater <dflater@nist.gov>
5
+ Copyright:: Public domain
6
+ License:: Unlicense
7
+
8
+ qdfca (Quick-Deploy Formal Concept Analysis) is a command-line filter that
9
+ implements Formal Concept Analysis (FCA). It is small, scriptable, and easy
10
+ to install, with no external requirements other than the standard Ruby
11
+ library. It is not designed to be efficient or scalable, but to ensure that
12
+ simple FCA tasks can be accomplished simply.
13
+
14
+ Usage: qdfca < context.csv > lattice.dot
15
+
16
+ The input is a formal context in comma-separated values (CSV) table format.
17
+ For example:
18
+
19
+ , A1, A2
20
+ Object 1, x,
21
+ Object 2, x, x
22
+
23
+ Attribute names are given by the first row. Object names are given by the
24
+ first column. An object is considered to have an attribute iff any
25
+ non-whitespace value appears in the corresponding table cell. The assumed
26
+ character encoding is UTF-8.
27
+
28
+ Most any spreadsheet application can be used to create and save a table in
29
+ this format. However, any use of escape characters or quoting to attempt to
30
+ allow values to contain commas is unsupported.
31
+
32
+ Output is produced in .dot file format to stdout.
33
+ Graphviz[http://www.graphviz.org/] and
34
+ xdot.py[https://github.com/jrfonseca/xdot.py] or other applications
35
+ supporting the DOT language can be used to view the output or render it into
36
+ various image formats.
37
+
38
+ The lattice is produced with reduced labelling. Concept labels list
39
+ attributes on the top line and objects on the bottom line if both are
40
+ applicable to a given node. Object names are parenthesized to avoid
41
+ ambiguity when only one line appears.
42
+
43
+ Four examples are installed along with the gem. On Linux they would show up
44
+ in /usr/lib64/ruby/gems/*/gems/qdfca-1/examples or a similar location.
45
+
46
+ == Legalese
47
+
48
+ Specific software products are identified in this documentation to support
49
+ reproducibility of results. Such identification does not imply
50
+ recommendation or endorsement by the National Institute of Standards and
51
+ Technology, nor does it imply that the products identified are necessarily
52
+ the best available for the purpose.
53
+
54
+ This software was developed at the National Institute of Standards and
55
+ Technology by an employee of the U.S. federal government in the course of his
56
+ official duties. Pursuant to Title 17 Section 105 of the United States Code,
57
+ this software is not subject to copyright protection and is in the public
58
+ domain.
@@ -0,0 +1,206 @@
1
+ #!/usr/bin/ruby
2
+ # 2015-02-20
3
+ #
4
+ # qdfca version 1.0
5
+ #
6
+ # A simple FCA tool for small problems. The input is a formal context in CSV
7
+ # table format. The output is a dot format digraph rendering of the concept
8
+ # lattice with reduced labelling.
9
+
10
+
11
+ # Internal data organization:
12
+ #
13
+ # Objects and attributes are identified by integers >= 0.
14
+ #
15
+ # Two different representations of objects and concepts are used. One is a
16
+ # set of attributes (integers >= 0). The other is a boolean array. The
17
+ # latter array's element i is true iff the former set includes i.
18
+ #
19
+ # Collections of objects and concepts are stored as arrays of sets.
20
+
21
+
22
+ unless ARGV.length==0
23
+ abort("Usage: qdfca < context.csv > lattice.dot")
24
+ end
25
+
26
+ require 'set'
27
+
28
+
29
+ # ---------------- Read the CSV format input ----------------
30
+
31
+ # First line of input: get attribute names and fix number of attributes.
32
+ line = gets
33
+ abort("qdfca: failed to read first line of input") unless line
34
+ columns = line.chomp.split(',',-1)
35
+ unless columns.length > 1
36
+ abort("qdfca: no attribute names found on first line of input")
37
+ end
38
+ attribute_names = columns[1..-1].map(&:strip)
39
+ $na1 = attribute_names.length
40
+ $na0 = $na1 - 1
41
+
42
+ # Remaining lines of input: get objects.
43
+ $objects = Array.new
44
+ object_names = Array.new
45
+ lineno = 2
46
+ line = gets
47
+ while line
48
+ columns = line.chomp.split(',',-1)
49
+ unless columns.length == $na1 + 1
50
+ abort("qdfca: wrong number of attributes found on line " + lineno.to_s)
51
+ end
52
+ object_names.push(columns[0].strip)
53
+ $objects.push(Set.new((0..$na0).find_all{|i| columns[i+1].strip.length > 0}))
54
+ lineno += 1
55
+ line = gets
56
+ end
57
+ $no1 = $objects.length
58
+ abort("qdfca: zero objects read") if $no1==0
59
+ $no0 = $no1-1
60
+
61
+
62
+ # ---------------- Define FCbO functions ----------------
63
+
64
+ # The FCbO functions computeClosure and generateFrom are translated from the
65
+ # sequential algorithm given in Petr Krajca, Jan Outrata, and Vilem Vychodil,
66
+ # "Parallel Recursive Algorithm for FCA," in Radim Belohlavek and Sergei
67
+ # O. Kuznetsov (eds.), Proceedings of the Sixth International Conference on
68
+ # Concept Lattices and their Applications (CLA 2008), pp. 71-82, October
69
+ # 2008.
70
+
71
+ # The following variables are comparable to those appearing in the cited FCbO
72
+ # functions:
73
+ # a, c ... boolean vector giving an extent.
74
+ # b, d ... boolean vector giving an intent.
75
+ # y ... an attribute number.
76
+
77
+ # The following variables appearing in the cited FCbO functions were replaced
78
+ # by equivalents:
79
+ # table ... 2D array of booleans: substituted references to objects.
80
+ # rows ... table slices: substituted an equivalent reference to objects.
81
+ # m ... number of objects/rows minus 1: substituted no0.
82
+ # n ... number of attributes/columns minus 1: substituted na0.
83
+
84
+ # Finally, the variable concepts was added to allow the results of
85
+ # generateFrom to be returned.
86
+
87
+ def computeClosure(a, b, y)
88
+ c = Array.new($no1, false)
89
+ d = Array.new($na1, true)
90
+ for i in 0..$no0
91
+ if a[i] and $objects[i].include?(y)
92
+ c[i] = true
93
+ for j in 0..$na0
94
+ unless $objects[i].include?(j)
95
+ d[j] = false
96
+ end
97
+ end
98
+ end
99
+ end
100
+ return c,d
101
+ end
102
+
103
+ def generateFrom(a, b, y)
104
+ concepts = Array.new(1){Set.new((0..$na0).find_all{|i| b[i]})}
105
+ if b.any?{|x| !x} and y<=$na0
106
+ for j in y..$na0
107
+ unless b[j]
108
+ c,d = computeClosure(a,b,j)
109
+ skip = false
110
+ for k in 0..(j-1)
111
+ if d[k] != b[k]
112
+ skip = true
113
+ break
114
+ end
115
+ end
116
+ unless skip
117
+ concepts += generateFrom(c,d,j+1)
118
+ end
119
+ end
120
+ end
121
+ end
122
+ return concepts
123
+ end
124
+
125
+
126
+ # ---------------- Find concepts ----------------
127
+
128
+ # Find all concepts using FCbO, starting with the infimum.
129
+ # Infimum extent = all objects.
130
+ # Infimum intent = what attributes all objects have.
131
+ infimum = $objects.reduce(&:&)
132
+ concepts = generateFrom(Array.new($no1,true),
133
+ Array.new($na1){|i| infimum.include?(i)},
134
+ 0)
135
+ nc1 = concepts.length
136
+ abort("Zero concepts") if nc1==0
137
+ nc0 = nc1-1
138
+
139
+
140
+ # ---------------- Apply reduced labelling ----------------
141
+
142
+ concept_o = Array.new(nc1){|i| Set.new}
143
+ for oi in 0..$no0
144
+ oci = -1
145
+ for ci in 0..nc0
146
+ if concepts[ci].subset?($objects[oi])
147
+ if oci<0 or concepts[oci].subset?(concepts[ci])
148
+ oci = ci
149
+ end
150
+ end
151
+ end
152
+ abort("qdfca: failure finding object concept") if oci<0
153
+ concept_o[oci].add(oi)
154
+ end
155
+ concept_a = Array.new(nc1){|i| Set.new}
156
+ for ai in 0..$na0
157
+ aci = -1
158
+ for ci in 0..nc0
159
+ if concepts[ci].include?(ai)
160
+ if aci<0 or concepts[ci].subset?(concepts[aci])
161
+ aci = ci
162
+ end
163
+ end
164
+ end
165
+ abort("qdfca: failure finding attribute concept") if aci<0
166
+ concept_a[aci].add(ai)
167
+ end
168
+ concept_names = Array.new(nc1){|ci|
169
+ onames = concept_o[ci].map{|i| object_names[i]}.sort.join(",")
170
+ anames = concept_a[ci].map{|i| attribute_names[i]}.sort.join(",")
171
+ anames + ((onames.length>0 and anames.length>0) ? "\\n" : "") +
172
+ (onames.length>0 ? "(" + onames + ")" : "")
173
+ }
174
+
175
+
176
+ # ---------------- Generate dot format output ----------------
177
+
178
+ # Although we don't use arrows, we still need a directed graph to keep the
179
+ # "lattice" properly sorted.
180
+ print "digraph {\n"
181
+ print " rankdir=TB\n"
182
+ print " node [style=filled, fillcolor=skyblue]\n"
183
+
184
+ # Define node IDs and labels. With reduced labelling there will be many
185
+ # nodes with neither objects nor attributes to indicate, and consequently
186
+ # empty labels, which cannot be distinguished without a separate ID.
187
+ for ci in 0..nc0
188
+ print " ", ci.to_s, " [label=\"", concept_names[ci], "\"]\n"
189
+ end
190
+
191
+ # Nodes (concepts) were found by FCbO, but we now use brute force and
192
+ # ignorance to find the lines. The lines correspond to the transitive
193
+ # reduction of the subset relationships. An alternative approach is to
194
+ # dumbly output every single subset relationship and then run the (maybe very
195
+ # large) result through graphviz's transitive reduction filter, tred.
196
+ for src in 0..nc0
197
+ for dst in 0..nc0
198
+ if concepts[src].proper_subset?(concepts[dst]) and
199
+ not concepts.any?{|spoiler|
200
+ concepts[src].proper_subset?(spoiler) and
201
+ spoiler.proper_subset?(concepts[dst])}
202
+ print " ", src.to_s, " -> ", dst.to_s, " [dir=none]\n"
203
+ end
204
+ end
205
+ end
206
+ print "}\n"
@@ -0,0 +1,5 @@
1
+ examples:
2
+ qdfca < e1/e1.csv > e1/e1.dot
3
+ qdfca < e2/e2.csv > e2/e2.dot
4
+ qdfca < e3/e3.csv > e3/e3.dot
5
+ qdfca < e4/e4.csv > e4/e4.dot
@@ -0,0 +1,5 @@
1
+ ,∼◇p,∼p,◇∼p,◇p,p,◽p
2
+ Contradiction,x,x,x,,,
3
+ Counterfactual,,x,x,x,,
4
+ Fact,,,x,x,x,
5
+ Tautology,,,,x,x,x
@@ -0,0 +1,31 @@
1
+ digraph {
2
+ rankdir=TB
3
+ node [style=filled, fillcolor=skyblue]
4
+ 0 [label=""]
5
+ 1 [label="∼◇p\n(Contradiction)"]
6
+ 2 [label=""]
7
+ 3 [label="∼p"]
8
+ 4 [label="(Counterfactual)"]
9
+ 5 [label="◇∼p"]
10
+ 6 [label=""]
11
+ 7 [label="(Fact)"]
12
+ 8 [label="◇p"]
13
+ 9 [label="p"]
14
+ 10 [label="◽p\n(Tautology)"]
15
+ 0 -> 5 [dir=none]
16
+ 0 -> 8 [dir=none]
17
+ 1 -> 2 [dir=none]
18
+ 3 -> 1 [dir=none]
19
+ 3 -> 4 [dir=none]
20
+ 4 -> 2 [dir=none]
21
+ 5 -> 3 [dir=none]
22
+ 5 -> 6 [dir=none]
23
+ 6 -> 4 [dir=none]
24
+ 6 -> 7 [dir=none]
25
+ 7 -> 2 [dir=none]
26
+ 8 -> 6 [dir=none]
27
+ 8 -> 9 [dir=none]
28
+ 9 -> 7 [dir=none]
29
+ 9 -> 10 [dir=none]
30
+ 10 -> 2 [dir=none]
31
+ }
Binary file
@@ -0,0 +1,11 @@
1
+ ,Know ∼◇p,Know ∼p,Know ◇∼p,Know ◇p,Know p,Know ◽p
2
+ Unknown,,,,,,
3
+ Negative possibility,,,x,,,
4
+ Possibility,,,,x,,
5
+ Falsehood,,x,x,,,
6
+ Uncertainty,,,x,x,,
7
+ Truth,,,,x,x,
8
+ Contradiction,x,x,x,,,
9
+ Counterfactual,,x,x,x,,
10
+ Fact,,,x,x,x,
11
+ Tautology,,,,x,x,x
@@ -0,0 +1,31 @@
1
+ digraph {
2
+ rankdir=TB
3
+ node [style=filled, fillcolor=skyblue]
4
+ 0 [label="(Unknown)"]
5
+ 1 [label="Know ∼◇p\n(Contradiction)"]
6
+ 2 [label=""]
7
+ 3 [label="Know ∼p\n(Falsehood)"]
8
+ 4 [label="(Counterfactual)"]
9
+ 5 [label="Know ◇∼p\n(Negative possibility)"]
10
+ 6 [label="(Uncertainty)"]
11
+ 7 [label="(Fact)"]
12
+ 8 [label="Know ◇p\n(Possibility)"]
13
+ 9 [label="Know p\n(Truth)"]
14
+ 10 [label="Know ◽p\n(Tautology)"]
15
+ 0 -> 5 [dir=none]
16
+ 0 -> 8 [dir=none]
17
+ 1 -> 2 [dir=none]
18
+ 3 -> 1 [dir=none]
19
+ 3 -> 4 [dir=none]
20
+ 4 -> 2 [dir=none]
21
+ 5 -> 3 [dir=none]
22
+ 5 -> 6 [dir=none]
23
+ 6 -> 4 [dir=none]
24
+ 6 -> 7 [dir=none]
25
+ 7 -> 2 [dir=none]
26
+ 8 -> 6 [dir=none]
27
+ 8 -> 9 [dir=none]
28
+ 9 -> 7 [dir=none]
29
+ 9 -> 10 [dir=none]
30
+ 10 -> 2 [dir=none]
31
+ }
Binary file
@@ -0,0 +1,3 @@
1
+ ,Attribute 1,Attribute 2
2
+ Thing 1,mumble,foo
3
+ Thing 2,not whitespace,“”
@@ -0,0 +1,5 @@
1
+ digraph {
2
+ rankdir=TB
3
+ node [style=filled, fillcolor=skyblue]
4
+ 0 [label="Attribute 1,Attribute 2\n(Thing 1,Thing 2)"]
5
+ }
Binary file
@@ -0,0 +1,5 @@
1
+ 2015-02-18 17:11
2
+
3
+ This example originally from Rudolf Wille as reproduced at
4
+ www.upriss.org.uk/fca/examples.html. A different version appears in the book
5
+ Formal Concept Analysis: Mathematical Foundations.
@@ -0,0 +1,9 @@
1
+ ,needs water to live,lives in water,lives on land,needs chlorophyll,dicotyledon,monocotyledon,can move,has limbs,breast feeds
2
+ fish leech,x,x,,,,,x,,
3
+ bream,x,x,,,,,x,x,
4
+ frog,x,x,x,,,,x,x,
5
+ dog,x,,x,,,,x,x,x
6
+ water weeds,x,x,,x,,x,,,
7
+ reed,x,x,x,x,,x,,,
8
+ bean,x,,x,x,x,,,,
9
+ corn,x,,x,x,,x,,,
@@ -0,0 +1,55 @@
1
+ digraph {
2
+ rankdir=TB
3
+ node [style=filled, fillcolor=skyblue]
4
+ 0 [label="needs water to live"]
5
+ 1 [label="lives in water"]
6
+ 2 [label=""]
7
+ 3 [label="(reed)"]
8
+ 4 [label=""]
9
+ 5 [label="(frog)"]
10
+ 6 [label="(water weeds)"]
11
+ 7 [label="(fish leech)"]
12
+ 8 [label="(bream)"]
13
+ 9 [label="lives on land"]
14
+ 10 [label=""]
15
+ 11 [label="dicotyledon\n(bean)"]
16
+ 12 [label="(corn)"]
17
+ 13 [label=""]
18
+ 14 [label="breast feeds\n(dog)"]
19
+ 15 [label="needs chlorophyll"]
20
+ 16 [label="monocotyledon"]
21
+ 17 [label="can move"]
22
+ 18 [label="has limbs"]
23
+ 0 -> 1 [dir=none]
24
+ 0 -> 9 [dir=none]
25
+ 0 -> 15 [dir=none]
26
+ 0 -> 17 [dir=none]
27
+ 1 -> 2 [dir=none]
28
+ 1 -> 6 [dir=none]
29
+ 1 -> 7 [dir=none]
30
+ 2 -> 3 [dir=none]
31
+ 2 -> 5 [dir=none]
32
+ 3 -> 4 [dir=none]
33
+ 5 -> 4 [dir=none]
34
+ 6 -> 3 [dir=none]
35
+ 7 -> 8 [dir=none]
36
+ 8 -> 5 [dir=none]
37
+ 9 -> 2 [dir=none]
38
+ 9 -> 10 [dir=none]
39
+ 9 -> 13 [dir=none]
40
+ 10 -> 11 [dir=none]
41
+ 10 -> 12 [dir=none]
42
+ 11 -> 4 [dir=none]
43
+ 12 -> 3 [dir=none]
44
+ 13 -> 5 [dir=none]
45
+ 13 -> 14 [dir=none]
46
+ 14 -> 4 [dir=none]
47
+ 15 -> 10 [dir=none]
48
+ 15 -> 16 [dir=none]
49
+ 16 -> 6 [dir=none]
50
+ 16 -> 12 [dir=none]
51
+ 17 -> 7 [dir=none]
52
+ 17 -> 18 [dir=none]
53
+ 18 -> 8 [dir=none]
54
+ 18 -> 13 [dir=none]
55
+ }
Binary file
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: qdfca
3
+ version: !ruby/object:Gem::Version
4
+ version: '1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - David Flater
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-02-20 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: qdfca (Quick-Deploy Formal Concept Analysis) is a command-line filter
15
+ that implements Formal Concept Analysis (FCA). It is small, scriptable, and easy
16
+ to install, with no external requirements other than the standard Ruby library. The
17
+ input is a formal context in CSV table format. The output is a dot format digraph
18
+ rendering of the concept lattice with reduced labelling.
19
+ email: dflater@nist.gov
20
+ executables:
21
+ - qdfca
22
+ extensions: []
23
+ extra_rdoc_files:
24
+ - README.rdoc
25
+ files:
26
+ - bin/qdfca
27
+ - README.rdoc
28
+ - examples/Makefile
29
+ - examples/e1/e1.dot
30
+ - examples/e1/e1.csv
31
+ - examples/e1/e1.ods
32
+ - examples/e2/e2.csv
33
+ - examples/e2/e2.ods
34
+ - examples/e2/e2.dot
35
+ - examples/e3/e3.ods
36
+ - examples/e3/e3.dot
37
+ - examples/e3/e3.csv
38
+ - examples/e4/README.txt
39
+ - examples/e4/e4.csv
40
+ - examples/e4/e4.ods
41
+ - examples/e4/e4.dot
42
+ homepage: http://www.nist.gov/itl/ssd/cs/software-performance.cfm
43
+ licenses:
44
+ - Unlicense
45
+ post_install_message:
46
+ rdoc_options:
47
+ - --main
48
+ - README.rdoc
49
+ - --title
50
+ - Quick-Deploy Formal Concept Analysis (FCA)
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ! '>='
57
+ - !ruby/object:Gem::Version
58
+ version: 1.9.3
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ requirements: []
66
+ rubyforge_project:
67
+ rubygems_version: 1.8.23
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: Quick-Deploy Formal Concept Analysis (FCA)
71
+ test_files: []