Как получить количество записей в журнале CSV в день - PullRequest
0 голосов
/ 01 апреля 2020

Если бы у меня был CSV-файл, похожий на следующий:

date,description,
Mon Jul 25 14:34:49 EDT 2016, some entry,
Mon Jul 25 17:35:11 EDT 2016, some other entry,
Mon Jul 25 21:52:05 EDT 2016, yet another entry,
Mon Jul 25 22:35:59 EDT 2016, some entry,
Mon Jul 25 23:54:19 EDT 2016, some other entry,
Tue Jul 26 00:31:41 EDT 2016, some entry,
Wed Jul 27 15:45:08 EDT 2016, yet another entry,
Wed Jul 27 16:15:50 EDT 2016, some entry,

Что было бы хорошим подходом для получения структуры данных, представляющей собой количество записей в день, чтобы я мог получить :

[{: date "2016-07-25": count 5} ...]

Я читаю CSV-файл и возвращаю число дней как JSON с использованием clojure. data.json / write-str, поэтому приведенная выше структура данных может быть лучше в другом формате.

1 Ответ

1 голос
/ 05 апреля 2020

Вот один из способов сделать это. Я добавил комментарии на каждом этапе конвейера:

(def lines "date,description,
Mon Jul 25 14:34:49 EDT 2016, some entry,
Mon Jul 25 17:35:11 EDT 2016, some other entry,
Mon Jul 25 21:52:05 EDT 2016, yet another entry,
Mon Jul 25 22:35:59 EDT 2016, some entry,
Mon Jul 25 23:54:19 EDT 2016, some other entry,
Tue Jul 26 00:31:41 EDT 2016, some entry,
Wed Jul 27 15:45:08 EDT 2016, yet another entry,
Wed Jul 27 16:15:50 EDT 2016, some entry,")

(->> ; get the lines
     lines  
     ; split them                                             
     clojure.string/split-lines   
     ; discard header                              
     rest            
     ; keep only the date                                           
     (map #(first (clojure.string/split % #",")))     
     ; make them Date instances          
     (map #(Date. %))          
     ; group them by yyyy-MM-dd                                
     (group-by #(.format (SimpleDateFormat. "yyyy-MM-dd") %))  
     ; make a map whose keys are {:date :count}  
     (reduce-kv #(assoc %1 %2 {:date %2 :count (count %3)}) {}) 
     ; keep only the values of this built map
     vals)                      

=> ({:date "2016-07-25", :count 2} {:date "2016-07-26", :count 4} {:date "2016-07-27", :count 2})

Другой способ использования frequencies вместо group-by:

(->> ; get the lines
     lines 
     ; split them
     clojure.string/split-lines rest
     ; keep the date
     (map #(first (clojure.string/split % #",")))
     ; format them as yyyy-MM-dd
     (map #(.format (SimpleDateFormat. "yyyy-MM-dd") (Date. %)))
     ; compute the frequencies
     frequencies
     ; build a map out of it, this time you don't need to count yourself since frequencies did it for you
     (reduce-kv #(assoc %1 %2 {:date %2 :count %3}) {})
     ; keep only the values of the map we built
     vals)

=> ({:date "2016-07-25", :count 2} {:date "2016-07-26", :count 4} {:date "2016-07-27", :count 2})

По производительности они похожи, на моем компьютере для анализа 100 000 строк требуется около 2 секунд.

Если узким местом является проблема производительности, вы можете создать только один SimpleDateFormat или даже отформатировать дату от строки до yyyy-MM-dd, не создавая Date вообще. Но только при необходимости производительность уже довольно приличная:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...