qdfca 1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: []