packages feed

Emping-0.4: src/CSVTable.hs

{- | Emping 0.4 (provisional)

Module CSVTable converts results to a CSV format that can be read by Open Office Calc
-}
module CSVTable ( allDup2CSVTb , allAmb2CSVTb, rnf2CSVTb, allAbd2CSVTb, topAbd2CSVTb ) where

import Data.Char (isDigit )
import Data.Array
import Data.Tree ( Tree(..), Forest )
import Aux (AVp, Ant, Rule)
import CSVParse (blankId)
--import Data.Tree ( Tree(..), Forest )

-- | check whether a string is a number or text
allDigit :: String -> Bool
allDigit [] = True
allDigit (x:xs) | isDigit x = allDigit xs
                | otherwise = False

-- | convert string or number to OO Calc format
toCalc :: String -> String
toCalc x | allDigit x = x
         | otherwise = "\"" ++ x ++ "\""

{- | get an OO Calc value String (-1 is \" \") from an attribute-value pair
Warning: for the time being all values of -1 are written as an NA field, not as blanks.
-}
showValue :: Array Int (String, [String]) -> AVp -> String
showValue attvarr  (att, val)
         | val == -1 = toCalc "NA"       -- blank field
         | otherwise = toCalc ((snd (attvarr ! att)) !! val)

-- | get an OO Calc value string from a Maybe AVp (Nothing is \" \")
showMaybeValue :: Array Int (String, [String]) -> Maybe AVp -> String
showMaybeValue attvarr mbav = 
   case mbav of 
        Just x -> showValue attvarr x
        Nothing -> " "

-- | get an OO Calc attribute string from an attribute index (blankId is \" \")
showAttribute :: Array Int (String, [String]) -> Int -> String
showAttribute attvarr att 
                   | attstr == blankId = " "
                   | otherwise = toCalc attstr
                   where attstr = fst (attvarr ! att)

-- | insert a separator s between two strings
inSep :: String -> String -> String -> String
inSep s x y = x ++ s ++ y

-- | from an ORIGINAL rule, get the display sequence (fst is the antecedent)
displayGuide :: Rule -> ([Int], Int)
displayGuide (ant, cons) =  (map fst ant, fst cons)

-- | lookup an attribute-value pair for an attribute in a reduced antecedent
lkp :: Int -> Ant -> Maybe AVp
lkp _ [] = Nothing
lkp att (x:xs) | att == fst x = Just x
               | otherwise = lkp att xs 

-- | order a reduced antecedent according to a display guide first, including Nothing
formatAnt :: [Int] -> Ant-> [Maybe AVp]
formatAnt [] _ = []
formatAnt (x:xs) antec = (lkp x antec): (formatAnt xs antec)

-- | get a list of OO Calc value strings from an antecedent
ant2CalStr :: Array Int (String, [String]) -> [Int] -> Ant -> [String]
ant2CalStr attvarr attls antec = 
          map (showMaybeValue attvarr) frmant where
                        frmant = formatAnt attls antec

-- | convert a reduced rule to a .csv table row (with \\n)
red2CSV :: Array Int (String,[String]) -> ([Int],Int) -> Rule -> String
red2CSV attvarr (attls, _ ) (ant,cons) =
                   (foldr1 (inSep ",") calstr) 
                   ++ ",\" is \"," 
                   ++ (showValue attvarr cons) 
                   ++ "\n" 
           where calstr = ant2CalStr attvarr attls ant

-- | convert the table header to a .csv row

hdr2CSV :: Array Int (String, [String]) -> ([Int], Int) -> String
hdr2CSV attvarr (attls, consatt) = 
                   (foldr1 (inSep ",") calstr) 
                   ++ ", ," 
                   ++ (showAttribute attvarr consatt)
                   ++  "\n"  
           where  calstr = map (showAttribute attvarr) attls
           
--------------------------------------------------

-- | convert a list of reductions with the same consequent to .csv
sameCons2CSV :: Array Int (String,[String]) -> ([Int], Int) -> [Rule] -> String
sameCons2CSV attvarr dispg redcons =
     concatMap (red2CSV attvarr dispg) redcons

-- | convert the reductions for all values of an attribute to .csv
allReds2CSV :: Array Int (String,[String]) -> ([Int], Int) -> [[Rule]] -> String
allReds2CSV attvarr dispg redgrp =
     concatMap (sameCons2CSV attvarr dispg) redgrp
--------------------------------------------------
{- | convert a reduced normal form to .csv table with a header. 

Warning: Get the display guide from the original rules
-}
rnf2CSVTb :: Array Int (String,[String]) -> [[Rule]] -> [[Rule]] -> String
rnf2CSVTb attvarr origs redgrp =
     (hdr2CSV attvarr dispg) ++ (allReds2CSV attvarr dispg redgrp) where
                        dispg = displayGuide (head (head origs))

----------------------- .csv output of equals ---------------

-- | convert a duplicate and its occurrence count into a .csv String 
dup2CSV :: Array Int (String, [String]) -> ([AVp], Int) -> String
dup2CSV attvarr (fct, cnt) = 
   (foldr1 (inSep ",") strls) ++ "," ++ ((toCalc . show) cnt) ++ "\n" where
                           strls = map (showValue attvarr) fct

-- | get a header from a duplicate and convert it  .csv String WITHOUT newline
dup2HdrCSV :: Array Int (String, [String]) -> ([AVp],Int) -> String
dup2HdrCSV attvarr (fct, _) = 
     (foldr1 (inSep ",") strls) ++ "," ++ (toCalc "Number") ++ "\n" where
                           strls = map ((showAttribute attvarr) . fst)  fct

-- | transform a list of representatives of duplicate and their counts to a .csv String
allDup2CSVTb :: Array Int (String,[String]) -> [([AVp],Int)] -> String
allDup2CSVTb attvarr dbls = (dup2HdrCSV attvarr (head dbls)) ++
                              concatMap (dup2CSV attvarr) dbls

-----------------------------------------------------
{- | show ambiguous original rules in .csv format

Note: original rules do not need intermediate Maybe's, like reduced rules
-}
org2CSV :: Array Int (String, [String]) -> Rule -> String
org2CSV attvarr orule = 
   antstr ++ ",\" is \"," ++ constr ++ "\n" where
        antstr = foldr1 (inSep ",") antstrls
        antstrls =  map (showValue attvarr) (fst orule)      
        constr = showValue attvarr (snd orule)

-- | ambiguous rules to .csv (always more than one) with extra newline

ambs2CSV :: Array Int (String,[String]) -> [Rule] -> String
ambs2CSV attvarr ambrls = 
     (concatMap (org2CSV attvarr) ambrls) ++ "\n"

-- | a group of ambiguous rules, with different consequents, to .csv
ambgrp2CSV :: Array Int (String,[String]) -> [[Rule]] -> String
ambgrp2CSV attvarr ambgrp 
    | length ambgrp == 1 = ambs2CSV attvarr (head ambgrp)
    | otherwise = concatMap (ambs2CSV attvarr) ambgrp

-- | display ambiguous rules in a .csv table. The guide for the header is obtained from the first rule.
allAmb2CSVTb :: Array Int (String,[String]) -> [[Rule]] -> String
allAmb2CSVTb attvarr ambgrp =
   (hdr2CSV attvarr (antatt,consatt)) ++ ambstr where
         (antatt, consatt) = ((map fst ant), (fst cons))
         (ant, cons) = head (head ambgrp)
         ambstr = ambgrp2CSV attvarr ambgrp
----------------------------------------------------------

-- ------------------------------------ The  .csv output of abductions ---------------------

-- | A general function to get the branches of a tree. Only used here, but could as well be in Aux.

brTree :: Tree a -> [[a]]
brTree (Node x []) = [[x]]
brTree (Node x for) = map (x:) brls where
                          brls = concatMap brTree for
---------------------------------------------------------------------------------------

-- | show the antecedent as a .csv string. Analogous to red2CSV, but without extras for the consequent
ant2CSV :: Array Int (String,[String]) -> [Int] -> Rule -> String
ant2CSV attvarr attls red = 
   foldr1 (inSep ",") calcs where 
       calcs =  ant2CalStr attvarr attls (fst red)

-- | show a list of equal antecedents in .csv format. Warning: as before, the guide is the first of a display guide, NO consequent yet.
eqs2CSV :: Array Int (String,[String]) -> [Int] -> [Rule] -> String
eqs2CSV  attvarr attls eqls 
        | length eqls == 1 = 
            ant2CSV attvarr attls (head eqls)
        | otherwise = 
            foldr1 (inSep ",\"equals\"\n") strls where
               strls = map (ant2CSV attvarr attls) eqls 

-- | show a chain of equals in .csv format.

chain2CSV :: Array Int (String,[String]) -> [Int] -> [[Rule]] -> String
chain2CSV attvarr attls chain
 | length chain == 1 = eqs2CSV attvarr attls (head chain)
 | otherwise = foldr1 (inSep ",\"implies\"\n") strls where
                 strls = map (eqs2CSV attvarr attls ) chain

-- | show a chain of implications, including equals, in .csv format. 
ruleChain2CSV :: Array Int (String,[String]) -> [Int] -> [[Rule]] ->  String
ruleChain2CSV attvarr attls chain  = (chain2CSV attvarr attls chain) 
                                      ++ ",\" is \","
                                      ++ (showValue attvarr ( snd (head (head chain))))
                                      ++ "\n\n"

-- | show a tree in .csv format. Warning: reverse is needed to get the most general rule at the end.
tree2CSV :: Array Int (String,[String]) -> [Int] -> Tree [Rule] -> String
tree2CSV attvarr attls t = 
     concatMap (ruleChain2CSV attvarr attls) chains where  chains = map reverse (brTree t)

-- | a forest of abduction trees to .csv format
forest2CSV :: Array Int (String,[String]) -> [Int] -> Forest [Rule] -> String
forest2CSV attvarr attls for = 
    concatMap (tree2CSV attvarr attls) for
-----------------------------------------------------
-- | abduction result to .csv format
allAbds2CSV :: Array Int (String,[String]) -> [Int] -> [Forest [Rule]]-> String
allAbds2CSV attvarr attls abdgrp =
    concatMap (forest2CSV attvarr attls) abdgrp

----------------------------------------------------
-- | transform the abduction result to .csv table with header 

allAbd2CSVTb :: Array Int (String,[String]) -> [[Rule]] -> [Forest [Rule]] -> String
allAbd2CSVTb attvarr origs abdgrp = (hdr2CSV attvarr guide) 
                                  ++ allAbds2CSV attvarr (fst guide) abdgrp where guide = displayGuide (head (head origs))

------------------------------------------ Top level abductions only ----------------------------------

-- | get a .csv string of (possibly equals) from the top only of a tree, including the consequent
topTree2CSV :: Array Int (String,[String]) -> [Int] -> Tree [Rule] -> String
topTree2CSV attvarr guide t =   let eqls = rootLabel t
                                in  (eqs2CSV attvarr guide eqls)
                                    ++ ",\" is \","
                                    ++ (showValue attvarr (snd (head eqls)))
                                    ++ "\n\n"   

-- | do it for the forest
topForest2CSV :: Array Int (String,[String]) -> [Int] -> Forest [Rule] -> String
topForest2CSV attvarr guide for = concatMap (topTree2CSV attvarr guide) for

-- | do it for all abductions
topAbds2CSV :: Array Int (String,[String]) -> [Int] -> [Forest [Rule]]-> String
topAbds2CSV attvarr guide abdgrp = concatMap (topForest2CSV attvarr guide) abdgrp

-- | add the header to the .csv table as with allAbd2CSVTb.  
topAbd2CSVTb :: Array Int (String,[String]) -> [[Rule]] -> [Forest [Rule]] -> String
topAbd2CSVTb attvarr origs abdgrp = (hdr2CSV attvarr guide) 
                                     ++ topAbds2CSV attvarr (fst guide) abdgrp where guide = displayGuide (head (head origs))
--------------------------------------------------------------------------------------------------------------------------