如何处理Haskell

时间:2018-06-14 21:59:03

标签: csv haskell

目标是读入文本文件并将其转换为csv。输入文本文件行将始终包含一些字段,但会有其他字段显示为零,1或更多次。问题是如何处理具有不同数量项目的那些字段。

问题示例:

我可以解析文本文件以获取可能如下所示的“事件”列表,其中每个数据构造函数对于特定类型的TrialEvents:

例如,

trialRecord1 = [ Trial {time = 123, trialNum = 1}
               , Efix {eye ='R' ,start = 123, stop = 234, x = 222, y = 123}
               , RewStart {time = 234}, RewEnd {time = 345} ]

trialRecord2 = [ Trial {time = 123, trialNum = 1}
               , RewStart {time = 234}, RewEnd {time = 345} ]

trialRecord3 = [ Trial {time = 123, trialNum = 1}
               , Efix {eye ='R' ,start = 123, stop = 234, x = 222, y = 123}
               , Efix {eye ='R' ,start = 223, stop = 334, x = 100, y = 222}
               , RewStart {time = 234}, RewEnd {time = 345} ]

这些事件列表(每个试验一个)将始终具有试用次数和时间,但可能包含0,1或更多其他值,例如本例中的Efix

我的困惑是关于如何从这样的数据生成csv文件,其中我可以有列头,例如trialTime,trialNumber,fixationStartTime,...,rewStartTime和RewEndTime。为了能够编写fixationStartTimes,我想我可以使用一个可能为空的列表,有一个值,或者有多个值。

但是当使用cassava包并用ToField编写我自己的pack $ show实例(只用整数列表测试)时,我注意到当列表大于length时我得到引号并转义字符1,但不适用于1或零长度列表。

"123,234,\"[1,2]\",345,456\r\n"
"123,234,[1],345,456\r\n"

当我尝试将csv文件读入分析程序时会出现问题,我将不得不处理这些不同的情况。

有人可以建议我如何在转换为csv时如何处理这个可变长度列表的问题,以及如何在将CSV读入R之类的其他环境时使其最大程度友好?

感谢。

1 个答案:

答案 0 :(得分:2)

  

有人可以建议我如何在转换为csv时如何处理这个可变长度列表的问题,以及如何在将CSV读入R之类的其他环境时使其最大程度友好?

由于这似乎是核心问题,并且该帖子的其余部分似乎是一个X-Y问题,假设CSV是首选格式,这里有一个孤立的答案:

如何使用JSON代替?您可以将数据类型编写为与您已经获得的JSON结构同构的JSON结构,并且R通过jsonlite支持JSON。然后,您可以在R中使用可变长度列表,而无需将它们编码为列布局(并再次返回?)。

但是,如果你更喜欢拥有那个列布局,那么这就是答案:

  

事件将始终具有试用次数和时间,但可能包含0,1或更多其他值,例如Efix [,RewStartRewEnd [ ?] ]在这个例子中。

然后

data Event = Trial { time :: Int, trialNum :: Int }
           | Efix { eye :: Char, start :: Int, stop :: Int, x :: Int, y :: Int }
           | RewStart { time :: Int }
           | RewEnd { time :: Int }

type Events = [Event]

似乎没有完全模仿你所说的。相反,

data Event p = Trial { time :: Int, trialNum :: Int, points :: [p] }
data Point = Efix { eye :: Char, start :: Int, stop :: Int, x :: Int, y :: Int }
           | RewStart { time :: Int }
           | RewEnd { time :: Int }

type Events = [Event Point]

然后你的记录看起来像

trialRecord1 = Trial { time = 123, trialNum = 1, points =
                 [ Efix { eye = 'R', start = 123, stop = 234, x = 222, y = 123 }
                 , RewStart { time = 234 }
                 , RewEnd { time = 345 } ] }

trialRecord2 = Trial { time = 123, trialNum = 1, points =
                 [ RewStart { time = 234 }
                 , RewEnd { time = 345 } ] }

trialRecord3 = Trial { time = 123, trialNum = 1, points =
                 [ Efix { eye = 'R', start = 123, stop = 234, x = 222, y = 123 }
                 , Efix { eye = 'R', start = 223, stop = 334, x = 100, y = 222 }
                 , RewStart { time = 234 }
                 , RewEnd { time = 345 } ] }
  

如何从这样的数据中生成csv文件,其中我可以使用 trialTime trialNum fixationStartTime 等列头。 。, rewStartTime rewEndTime

由于您只能确定 trialTime trialNum ,因此这些是您可以硬编码的唯一两列。其余列必须根据其他事件中的点进行缩进。例如,在表格布局中呈现trialRecord1trialRecord2trialRecord3应该(可能?)给出类似

的内容
+-----------+-----------+----------+--------------+--------------------+-------------------+------------+------------+--------------+--------------------+-------------------+------------+------------+---------------+-------------+
| recordNum | trialTime | trialNum | fixationEye1 | fixationStartTime1 | fixationStopTime1 | fixationX1 | fixationY1 | fixationEye2 | fixationStartTime2 | fixationStopTime2 | fixationX2 | fixationY2 | rewStartTime1 | rewEndTime1 |
+-----------+-----------+----------+--------------+--------------------+-------------------+------------+------------+--------------+--------------------+-------------------+------------+------------+---------------+-------------+
|         1 |       123 |        1 |            R |                123 |               234 |        222 |        123 |              |                    |                   |            |            |           234 |         345 |
|         2 |       123 |        1 |              |                    |                   |            |            |              |                    |                   |            |            |           234 |         345 |
|         3 |       123 |        1 |            R |                123 |               234 |        222 |        123 |            R |                223 |               334 |        100 |        222 |           234 |         345 |
+-----------+-----------+----------+--------------+--------------------+-------------------+------------+------------+--------------+--------------------+-------------------+------------+------------+---------------+-------------+

您可以编写一个函数align :: [Event Point] -> [Event (Maybe Point)],以便在数据丢失的情况下插入Nothing。 (Nothing可能对应于可变数量的行,具体取决于Point被转换为列的内容,因此您还可以考虑[Event Point] -> [Event (Either NumEmptyColumns Point)]类型type NumEmptyColumns = Int的函数。 )

然后运行align [ trialRecord1, trialRecord2, trialRecord3 ]可以给出值

[ Trial { time = 123, trialNum = 1, points =
    [ Just $ Efix { eye ='R', start = 123, stop = 234, x = 222, y = 123 }
    , Nothing
    , Just $ RewStart { time = 234 }
    , Just $ RewEnd { time = 345 } ] }

, Trial { time = 123, trialNum = 1, points =
    [ Nothing
    , Nothing
    , Just $ RewStart { time = 234 }
    , Just $ RewEnd { time = 345 } ] }

, Trial { time = 123, trialNum = 1, points =
    [ Just $ Efix { eye ='R', start = 123, stop = 234, x = 222, y = 123 }
    , Just $ Efix { eye ='R', start = 223, stop = 334, x = 100, y = 222 }
    , Just $ RewStart { time = 234 }
    , Just $ RewEnd { time = 345 } ] }
]

将这个常规(非锯齿状)列表列表转换为常规csv应该更直截了当。

  

pack $ show我注意到当列表大于长度1时我得到引号并转义字符

"123,234,\"[1,2]\",345,456\r\n"

正如@DarthFennec所说,这是因为值[1,2]包含一个逗号,它是csv中的特殊字符。这里唯一的转义字符是" s - \是Haskell转义码,用于显示包含引号的字符串:

GHCi> putStrLn "123,234,\"[1,2]\",345,456\r\n"
123,234,"[1,2]",345,456

这就是字符串的实际外观。

但是在csv文件中的Haskell语法列表文字中有多个值可能不是“最大程度友好”。如果你这样做,那么也许JSON是一个更好的选择。