0% found this document useful (0 votes)
458 views78 pages

Clojure Data Analysis Cookbook 2nd Edition Edition Eric Rochester - Download The Ebook Today and Own The Complete Content

The document provides information about the 'Clojure Data Analysis Cookbook, 2nd Edition' by Eric Rochester, which offers over 100 practical recipes for data analysis using Clojure. It includes details on various data analysis techniques, cleaning and validating data, and working with different data formats. Additionally, it lists other related ebooks available for instant download at ebookgate.com.

Uploaded by

palttarojeh63
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
458 views78 pages

Clojure Data Analysis Cookbook 2nd Edition Edition Eric Rochester - Download The Ebook Today and Own The Complete Content

The document provides information about the 'Clojure Data Analysis Cookbook, 2nd Edition' by Eric Rochester, which offers over 100 practical recipes for data analysis using Clojure. It includes details on various data analysis techniques, cleaning and validating data, and working with different data formats. Additionally, it lists other related ebooks available for instant download at ebookgate.com.

Uploaded by

palttarojeh63
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 78

Instant Ebook Access, One Click Away – Begin at ebookgate.

com

Clojure Data Analysis Cookbook 2nd Edition Edition


Eric Rochester

https://ebookgate.com/product/clojure-data-analysis-
cookbook-2nd-edition-edition-eric-rochester/

OR CLICK BUTTON

DOWLOAD EBOOK

Get Instant Ebook Downloads – Browse at https://ebookgate.com


Click here to visit ebookgate.com and download ebook now
Instant digital products (PDF, ePub, MOBI) available
Download now and explore formats that suit you...

Scala Data Analysis Cookbook Navigate the world of data


analysis visualization and machine learning with over 100
hands on Scala recipes 1st Edition Arun Manivannan
https://ebookgate.com/product/scala-data-analysis-cookbook-navigate-
the-world-of-data-analysis-visualization-and-machine-learning-with-
over-100-hands-on-scala-recipes-1st-edition-arun-manivannan/
ebookgate.com

Analysis of Economic Data 2nd Edition Gary Koop

https://ebookgate.com/product/analysis-of-economic-data-2nd-edition-
gary-koop/

ebookgate.com

Data Collection and Analysis 2nd Edition Roger Sapsford

https://ebookgate.com/product/data-collection-and-analysis-2nd-
edition-roger-sapsford/

ebookgate.com

Data Analysis for Physical Scientists Featuring Excel 2nd


Edition Les Kirkup

https://ebookgate.com/product/data-analysis-for-physical-scientists-
featuring-excel-2nd-edition-les-kirkup/

ebookgate.com
Statistics and Data Analysis for Nursing Research 2nd
Edition Denise Polit

https://ebookgate.com/product/statistics-and-data-analysis-for-
nursing-research-2nd-edition-denise-polit/

ebookgate.com

The Joy of Clojure Thinking the Clojure Way 1st Edition


Michael Fogus

https://ebookgate.com/product/the-joy-of-clojure-thinking-the-clojure-
way-1st-edition-michael-fogus/

ebookgate.com

HTML5 Graphing and Data Visualization Cookbook 1st Edition


Ben Fhala

https://ebookgate.com/product/html5-graphing-and-data-visualization-
cookbook-1st-edition-ben-fhala/

ebookgate.com

Categorical Data Analysis Using the SAS System 2nd Edition


Maura E. Stokes

https://ebookgate.com/product/categorical-data-analysis-using-the-sas-
system-2nd-edition-maura-e-stokes/

ebookgate.com

Microarrays Volume 2 Applications and Data Analysis 2nd


Edition Conor W. Sipe

https://ebookgate.com/product/microarrays-volume-2-applications-and-
data-analysis-2nd-edition-conor-w-sipe/

ebookgate.com
Clojure Data Analysis
Cookbook
Second Edition

Dive into data analysis with Clojure through over 100


practical recipes for every stage of the analysis and
collection process

Eric Rochester

BIRMINGHAM - MUMBAI
Clojure Data Analysis Cookbook
Second Edition

Copyright © 2015 Packt Publishing

All rights reserved. No part of this book may be reproduced, stored in a retrieval system,
or transmitted in any form or by any means, without the prior written permission of the
publisher, except in the case of brief quotations embedded in critical articles or reviews.

Every effort has been made in the preparation of this book to ensure the accuracy of the
information presented. However, the information contained in this book is sold without
warranty, either express or implied. Neither the author, nor Packt Publishing, and its dealers
and distributors will be held liable for any damages caused or alleged to be caused directly
or indirectly by this book.

Packt Publishing has endeavored to provide trademark information about all of the
companies and products mentioned in this book by the appropriate use of capitals.
However, Packt Publishing cannot guarantee the accuracy of this information.

First published: March 2013

Second edition: January 2015

Production reference: 1220115

Published by Packt Publishing Ltd.


Livery Place
35 Livery Street
Birmingham B3 2PB, UK.

ISBN 978-1-78439-029-7

www.packtpub.com
Credits

Author Project Coordinator


Eric Rochester Neha Thakur

Reviewers Proofreaders
Vitomir Kovanovic Ameesha Green
Muktabh Mayank Srivastava Joel T. Johnson
Federico Tomassetti Samantha Lyon

Commissioning Editor Indexer


Ashwin Nair Priya Sane

Acquisition Editor Graphics


Sam Wood Sheetal Aute
Disha Haria
Content Development Editor
Parita Khedekar Production Coordinator
Nitesh Thakur
Technical Editor
Ryan Kochery Cover Work
Nitesh Thakur
Copy Editors
Dipti Kapadia
Puja Lalwani
Vikrant Phadke
About the Author

Eric Rochester enjoys reading, writing, and spending time with his wife and kids. When
he’s not doing these things, he programs in a variety of languages and platforms, including
websites and systems in Python, and libraries for linguistics and statistics in C#. Currently,
he is exploring functional programming languages, including Clojure and Haskell. He works
at Scholars’ Lab in the library at the University of Virginia, helping humanities professors and
graduate students realize their digitally informed research agendas. He is also the author of
Mastering Clojure Data Analysis, Packt Publishing.

I’d like to thank everyone. My technical reviewers proved invaluable.


Also, thank you to the editorial staff at Packt Publishing. This book is
much stronger because of all of their feedback, and any remaining
deficiencies are mine alone.

A special thanks to Jackie, Melina, and Micah. They’ve been patient and
supportive while I worked on this project. It is, in every way, for them.
About the Reviewers

Vitomir Kovanovic is a PhD student at the School of Informatics, University of Edinburgh,


Edinburgh, UK. He received an MSc degree in computer science and software engineering
in 2011, and BSc in information systems and business administration in 2009 from the
University of Belgrade, Serbia. His research interests include learning analytics, educational
data mining, and online education. He is a member of the Society for Learning Analytics
Research and a member of program committees of several conferences and journals in
technology-enhanced learning. In his PhD research, he focuses on the use of trace data for
understanding the effects of technology use on the quality of the social learning process and
learning outcomes. For more information, visit http://vitomir.kovanovic.info/

Muktabh Mayank Srivastava is a data scientist and the cofounder of ParallelDots.com.


Previously, he helped in solving many complex data analysis and machine learning problems
for clients from different domains such as healthcare, retail, procurement, automation,
Bitcoin, social recommendation engines, geolocation fact-finding, customer profiling,
and so on.

His new venture is ParallelDots. It is a tool that allows any content archive to be presented
in a story using advanced techniques of NLP and machine learning. For publishers and
bloggers, it automatically creates a timeline of any event using their archive and presents
it in an interactive, intuitive, and easy-to-navigate interface on their webpage. You can find
him on LinkedIn at http://in.linkedin.com/in/muktabh/ and on Twitter at
@muktabh / @ParallelDots.
Federico Tomassetti has been programming since he was a child and has a PhD
in software engineering. He works as a consultant on model-driven development and
domain-specific languages, writes technical articles, teaches programming, and works as
a full-stack software engineer.

He has experience working in Italy, Germany, and Ireland, and he is currently working
at Groupon International.

You can read about his projects on http://federico-tomassetti.it/ or


https://github.com/ftomassetti/.
www.PacktPub.com

Support files, eBooks, discount offers, and more


For support files and downloads related to your book, please visit www.PacktPub.com.

Did you know that Packt offers eBook versions of every book published, with PDF and ePub
files available? You can upgrade to the eBook version at www.PacktPub.com and as a print
book customer, you are entitled to a discount on the eBook copy. Get in touch with us at
[email protected] for more details.

At www.PacktPub.com, you can also read a collection of free technical articles, sign up
for a range of free newsletters and receive exclusive discounts and offers on Packt books
and eBooks.
TM

https://www2.packtpub.com/books/subscription/packtlib

Do you need instant solutions to your IT questions? PacktLib is Packt’s online digital book
library. Here, you can search, access, and read Packt’s entire library of books.

Why subscribe?
ff Fully searchable across every book published by Packt
ff Copy and paste, print, and bookmark content
ff On demand and accessible via a web browser

Free access for Packt account holders


If you have an account with Packt at www.PacktPub.com, you can use this to access
PacktLib today and view 9 entirely free books. Simply use your login credentials for
immediate access.
Table of Contents
Preface 1
Chapter 1: Importing Data for Analysis 7
Introduction 7
Creating a new project 8
Reading CSV data into Incanter datasets 9
Reading JSON data into Incanter datasets 12
Reading data from Excel with Incanter 14
Reading data from JDBC databases 15
Reading XML data into Incanter datasets 18
Scraping data from tables in web pages 21
Scraping textual data from web pages 25
Reading RDF data 29
Querying RDF data with SPARQL 33
Aggregating data from different formats 38
Chapter 2: Cleaning and Validating Data 45
Introduction 45
Cleaning data with regular expressions 46
Maintaining consistency with synonym maps 48
Identifying and removing duplicate data 50
Regularizing numbers 53
Calculating relative values 55
Parsing dates and times 57
Lazily processing very large data sets 59
Sampling from very large data sets 61
Fixing spelling errors 64
Parsing custom data formats 68
Validating data with Valip 70
Table of Contents

Chapter 3: Managing Complexity with Concurrent Programming 73


Introduction 74
Managing program complexity with STM 75
Managing program complexity with agents 79
Getting better performance with commute 82
Combining agents and STM 83
Maintaining consistency with ensure 85
Introducing safe side effects into the STM 88
Maintaining data consistency with validators 91
Monitoring processing with watchers 94
Debugging concurrent programs with watchers 96
Recovering from errors in agents 98
Managing large inputs with sized queues 100
Chapter 4: Improving Performance with Parallel Programming 101
Introduction 102
Parallelizing processing with pmap 102
Parallelizing processing with Incanter 106
Partitioning Monte Carlo simulations for better pmap performance 107
Finding the optimal partition size with simulated annealing 112
Combining function calls with reducers 116
Parallelizing with reducers 118
Generating online summary statistics for data streams with reducers 121
Using type hints 124
Benchmarking with Criterium 127
Chapter 5: Distributed Data Processing with Cascalog 131
Introduction 131
Initializing Cascalog and Hadoop for distributed processing 133
Querying data with Cascalog 137
Distributing data with Apache HDFS 138
Parsing CSV files with Cascalog 141
Executing complex queries with Cascalog 143
Aggregating data with Cascalog 146
Defining new Cascalog operators 148
Composing Cascalog queries 151
Transforming data with Cascalog 153

ii
Table of Contents

Chapter 6: Working with Incanter Datasets 155


Introduction 156
Loading Incanter's sample datasets 156
Loading Clojure data structures into datasets 157
Viewing datasets interactively with view 159
Converting datasets to matrices 161
Using infix formulas in Incanter 163
Selecting columns with $ 165
Selecting rows with $ 167
Filtering datasets with $where 169
Grouping data with $group-by 170
Saving datasets to CSV and JSON 172
Projecting from multiple datasets with $join 173
Chapter 7: Statistical Data Analysis with Incanter 177
Introduction 177
Generating summary statistics with $rollup 178
Working with changes in values 180
Scaling variables to simplify variable relationships 182
Working with time series data with Incanter Zoo 184
Smoothing variables to decrease variation 186
Validating sample statistics with bootstrapping 189
Modeling linear relationships 192
Modeling non-linear relationships 195
Modeling multinomial Bayesian distributions 199
Finding data errors with Benford's law 202
Chapter 8: Working with Mathematica and R 207
Introduction 207
Setting up Mathematica to talk to Clojuratica for Mac OS X and Linux 208
Setting up Mathematica to talk to Clojuratica for Windows 212
Calling Mathematica functions from Clojuratica 214
Sending matrixes to Mathematica from Clojuratica 215
Evaluating Mathematica scripts from Clojuratica 217
Creating functions from Mathematica 218
Setting up R to talk to Clojure 219
Calling R functions from Clojure 221
Passing vectors into R 222
Evaluating R files from Clojure 224
Plotting in R from Clojure 226

iii
Table of Contents

Chapter 9: Clustering, Classifying, and Working with Weka 229


Introduction 229
Loading CSV and ARFF files into Weka 230
Filtering, renaming, and deleting columns in Weka datasets 232
Discovering groups of data using K-Means clustering 235
Finding hierarchical clusters in Weka 241
Clustering with SOMs in Incanter 244
Classifying data with decision trees 246
Classifying data with the Naive Bayesian classifier 249
Classifying data with support vector machines 251
Finding associations in data with the Apriori algorithm 254
Chapter 10: Working with Unstructured and Textual Data 257
Introduction 258
Tokenizing text 258
Finding sentences 259
Focusing on content words with stoplists 260
Getting document frequencies 262
Scaling document frequencies by document size 264
Scaling document frequencies with TF-IDF 266
Finding people, places, and things with Named Entity Recognition 270
Mapping documents to a sparse vector space representation 272
Performing topic modeling with MALLET 274
Performing naïve Bayesian classification with MALLET 277
Chapter 11: Graphing in Incanter 281
Introduction 281
Creating scatter plots with Incanter 282
Graphing non-numeric data in bar charts 284
Creating histograms with Incanter 287
Creating function plots with Incanter 288
Adding equations to Incanter charts 290
Adding lines to scatter charts 292
Customizing charts with JFreeChart 293
Customizing chart colors and styles 296
Saving Incanter graphs to PNG 298
Using PCA to graph multi-dimensional data 299
Creating dynamic charts with Incanter 302

iv
Table of Contents

Chapter 12: Creating Charts for the Web 305


Introduction 305
Serving data with Ring and Compojure 306
Creating HTML with Hiccup 311
Setting up to use ClojureScript 313
Creating scatter plots with NVD3 317
Creating bar charts with NVD3 323
Creating histograms with NVD3 326
Creating time series charts with D3 329
Visualizing graphs with force-directed layouts 334
Creating interactive visualizations with D3 339
Index 345

v
Preface
Welcome to the second edition of Clojure Data Analysis Cookbook! It seems that books
become obsolete almost as quickly as software does, so here we have the opportunity to
keep things up-to-date and useful.

Moreover, the state of the art of data analysis is also still evolving and changing. The
techniques and technologies are being refined and improved. Hopefully, this book will capture
some of that. I've also added a new chapter on how to work with unstructured textual data.

In spite of these changes, some things have stayed the same. Clojure has further proven
itself to be an excellent environment to work with data. As a member of the lisp family of
languages, it inherits a flexibility and power that is hard to match. The concurrency and
parallelization features have further proven themselves as great tools for developing
software and analyzing data.

Clojure's usefulness for data analysis is further improved by a number of strong libraries.
Incanter provides a practical environment to work with data and perform statistical analysis.
Cascalog is an easy-to-use wrapper over Hadoop and Cascading. Finally, when you're ready
to publish your results, ClojureScript, an implementation of Clojure that generates JavaScript,
can help you to visualize your data in an effective and persuasive way.

Moreover, Clojure runs on the Java Virtual Machine (JVM), so any libraries written for Java are
available too. This gives Clojure an incredible amount of breadth and power.

I hope that this book will give you the tools and techniques you need to get answers from
your data.
Preface

What this book covers


Chapter 1, Importing Data for Analysis, covers how to read data from a variety of sources,
including CSV files, web pages, and linked semantic web data.

Chapter 2, Cleaning and Validating Data, presents strategies and implementations to


normalize dates, fix spelling, and work with large datasets. Getting data into a useable shape
is an important, but often overlooked, stage of data analysis.

Chapter 3, Managing Complexity with Concurrent Programming, covers Clojure's concurrency


features and how you can use them to simplify your programs.

Chapter 4, Improving Performance with Parallel Programming, covers how to use Clojure's
parallel processing capabilities to speed up the processing of data.

Chapter 5, Distributed Data Processing with Cascalog, covers how to use Cascalog as a
wrapper over Hadoop and the Cascading library to process large amounts of data distributed
over multiple computers.

Chapter 6, Working with Incanter Datasets, covers the basics of working with Incanter
datasets. Datasets are the core data structures used by Incanter, and understanding them is
necessary in order to use Incanter effectively.

Chapter 7, Statistical Data Analysis with Incanter, covers a variety of statistical processes and
tests used in data analysis. Some of these are quite simple, such as generating summary
statistics. Others are more complex, such as performing linear regressions and auditing data
with Benford's Law.

Chapter 8, Working with Mathematica and R, talks about how to set up Clojure in order to talk
to Mathematica or R. These are powerful data analysis systems, and we might want to use
them sometimes. This chapter will show you how to get these systems to work together, as
well as some tasks that you can perform once they are communicating.

Chapter 9, Clustering, Classifying, and Working with Weka, covers more advanced machine
learning techniques. In this chapter, we'll primarily use the Weka machine learning library.
Some recipes will discuss how to use it and the data structures its built on, while other recipes
will demonstrate machine learning algorithms.

Chapter 10, Working with Unstructured and Textual Data, looks at tools and techniques used
to extract information from the reams of unstructured, textual data.

Chapter 11, Graphing in Incanter, shows you how to generate graphs and other visualizations
in Incanter. These can be important for exploring and learning about your data and also for
publishing and presenting your results.

Chapter 12, Creating Charts for the Web, shows you how to set up a simple web application in
order to present findings from data analysis. It will include a number of recipes that leverage
the powerful D3 visualization library.
2
Preface

What you need for this book


One piece of software required for this book is the Java Development Kit (JDK), which you
can obtain from http://www.oracle.com/technetwork/java/javase/downloads/
index.html. JDK is necessary to run and develop on the Java platform.

The other major piece of software that you'll need is Leiningen 2, which you can download
and install from http://leiningen.org/. Leiningen 2 is a tool used to manage Clojure
projects and their dependencies. It has become the de facto standard project tool in the
Clojure community.

Throughout this book, we'll use a number of other Clojure and Java libraries, including Clojure
itself. Leiningen will take care of downloading these for us as we need them.

You'll also need a text editor or Integrated Development Environment (IDE). If you already have
a text editor of your choice, you can probably use it. See http://clojure.org/getting_
started for tips and plugins for using your particular favorite environment. If you don't have a
preference, I'd suggest that you take a look at using Eclipse with Counterclockwise. There are
instructions to this set up at https://code.google.com/p/counterclockwise/.

That is all that's required. However, at various places throughout the book, some recipes will
access other software. The recipes in Chapter 8, Working with Mathematica and R, that are
related to Mathematica will require Mathematica, obviously, and those that are related to R
will require that. However, these programs won't be used in the rest of the book, and whether
you're interested in those recipes might depend on whether you already have this software.

Who this book is for


This book is for programmers or data scientists who are familiar with Clojure and want to use
it in their data analysis processes. This isn't a tutorial on Clojure—there are already a number
of excellent introductory books out there—so you'll need to be familiar with the language,
but you don't need to be an expert.

Likewise, you don't have to be an expert on data analysis, although you should probably be
familiar with its tasks, processes, and techniques. While you might be able to glean enough
from these recipes to get started with, for it to be truly effective, you'll want to get a more
thorough introduction to this field.

3
Preface

Conventions
In this book, you will find a number of styles of text that distinguish between different kinds of
information. Here are some examples of these styles, and an explanation of their meaning.

Code words in text, database table names, folder names, filenames, file extensions,
pathnames, dummy URLs, user input, and Twitter handles are shown as follows: "Now, there
will be a new subdirectory named getting-data.

A block of code is set as follows:


(defproject getting-data "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.6.0"]])

When we wish to draw your attention to a particular part of a code block, the relevant lines or
items are set in bold:
(defn watch-debugging
[input-file]
(let [reader (agent
(seque
(mapcat
lazy-read-csv
input-files)))
caster (agent nil)
sink (agent [])
counter (ref 0)
done (ref false)]
(add-watch caster :counter
(partial watch-caster counter))
(add-watch caster :debug debug-watch)
(send reader read-row caster sink done)
(wait-for-it 250 done)
{:results @sink
:count-watcher @counter}))

Any command-line input or output is written as follows:


$ lein new getting-data
Generating a project called getting-data based on the default template.
To see other templates (app, lein plugin, etc), try lein help new.

4
Preface

New terms and important words are shown in bold. Words that you see on the screen,
in menus or dialog boxes for example, appear in the text like this: "Take a look at the
Hadoop website for the Getting Started documentation of your version. Get a single
node setup working".

Warnings or important notes appear in a box like this.

Tips and tricks appear like this.

Reader feedback
Feedback from our readers is always welcome. Let us know what you think about this
book—what you liked or may have disliked. Reader feedback is important for us to develop
titles that you really get the most out of.

To send us general feedback, simply send an e-mail to [email protected], and


mention the book title via the subject of your message.

If there is a topic that you have expertise in and you are interested in either writing or
contributing to a book, see our author guide on www.packtpub.com/authors.

Customer support
Now that you are the proud owner of a Packt book, we have a number of things to help you to
get the most from your purchase.

Downloading the example code


You can download the example code files for all Packt books you have purchased from
your account at http://www.packtpub.com. If you purchased this book elsewhere,
you can visit http://www.packtpub.com/support and register to have the files
e-mailed directly to you.

5
Preface

Downloading the color images of this book


We also provide you a PDF file that has color images of the screenshots/diagrams used in
this book. The color images will help you better understand the changes in the output. You
can download this file from: https://www.packtpub.com/sites/default/files/
downloads/B03480_coloredimages.pdf.

Errata
Although we have taken every care to ensure the accuracy of our content, mistakes do happen.
If you find a mistake in one of our books—maybe a mistake in the text or the code—we would be
grateful if you could report this to us. By doing so, you can save other readers from frustration
and help us improve subsequent versions of this book. If you find any errata, please report them
by visiting http://www.packtpub.com/submit-errata, selecting your book, clicking on
the Errata Submission Form link, and entering the details of your errata. Once your errata are
verified, your submission will be accepted and the errata will be uploaded to our website or
added to any list of existing errata under the Errata section of that title.

To view the previously submitted errata, go to https://www.packtpub.com/books/


content/support and enter the name of the book in the search field. The required
information will appear under the Errata section.

Piracy
Piracy of copyright material on the Internet is an ongoing problem across all media. At Packt,
we take the protection of our copyright and licenses very seriously. If you come across any
illegal copies of our works, in any form, on the Internet, please provide us with the location
address or website name immediately so that we can pursue a remedy.

Please contact us at [email protected] with a link to the suspected


pirated material.

We appreciate your help in protecting our authors, and our ability to bring you
valuable content.

Questions
You can contact us at [email protected] if you are having a problem with any
aspect of the book, and we will do our best to address it.

6
Importing Data for
1
Analysis
In this chapter, we will cover the following recipes:

ff Creating a new project


ff Reading CSV data into Incanter datasets
ff Reading JSON data into Incanter datasets
ff Reading data from Excel with Incanter
ff Reading data from JDBC databases
ff Reading XML data into Incanter datasets
ff Scraping data from tables in web pages
ff Scraping textual data from web pages
ff Reading RDF data
ff Querying RDF data with SPARQL
ff Aggregating data from different formats

Introduction
There's not much data analysis that can be done without data, so the first step in any project
is to evaluate the data we have and the data that we need. Once we have some idea of what
we'll need, we have to figure out how to get it.
Importing Data for Analysis

Many of the recipes in this chapter and in this book use Incanter (http://incanter.org/)
to import the data and target Incanter datasets. Incanter is a library that is used for statistical
analysis and graphics in Clojure (similar to R) an open source language for statistical
computing (http://www.r-project.org/). Incanter might not be suitable for every task
(for example, we'll use the Weka library for machine learning later) but it is still an important
part of our toolkit for doing data analysis in Clojure. This chapter has a collection of recipes
that can be used to gather data and make it accessible to Clojure.

For the very first recipe, we'll take a look at how to start a new project. We'll start with very
simple formats such as comma-separated values (CSV) and move into reading data from
relational databases using JDBC. We'll examine more complicated data sources, such as
web scraping and linked data (RDF).

Creating a new project


Over the course of this book, we're going to use a number of third-party libraries and external
dependencies. We will need a tool to download them and track them. We also need a tool to
set up the environment and start a REPL (read-eval-print-loop or interactive interpreter) that
can access our code or to execute our program. REPLs allow you to program interactively. It's a
great environment for exploratory programming, irrespective of whether that means exploring
library APIs or exploring data.

We'll use Leiningen for this (http://leiningen.org/). This has become a standard
package automation and management system.

Getting ready
Visit the Leiningen site and download the lein script. This will download the Leiningen JAR
file when it's needed. The instructions are clear, and it's a simple process.

How to do it...
To generate a new project, use the lein new command, passing the name of the project
to it:
$ lein new getting-data
Generating a project called getting-data based on the default template.
To see other templates (app, lein plugin, etc), try lein help new.

There will be a new subdirectory named getting-data. It will contain files with stubs for the
getting-data.core namespace and for tests.

8
Chapter 1

How it works...
The new project directory also contains a file named project.clj. This file contains
metadata about the project, such as its name, version, license, and more. It also contains
a list of the dependencies that our code will use, as shown in the following snippet. The
specifications that this file uses allow it to search Maven repositories and directories of
Clojure libraries (Clojars, https://clojars.org/) in order to download the project's
dependencies. Thus, it integrates well with Java's own packaging system as developed with
Maven (http://maven.apache.org/).
(defproject getting-data "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.6.0"]])

In the Getting ready section of each recipe, we'll see the libraries that we need to list in the
:dependencies section of this file. Then, when you run any lein command, it will download
the dependencies first.

Reading CSV data into Incanter datasets


One of the simplest data formats is comma-separated values (CSV), and you'll find that
it's everywhere. Excel reads and writes CSV directly, as do most databases. Also, because
it's really just plain text, it's easy to generate CSV files or to access them from any
programming language.

Getting ready
First, let's make sure that we have the correct libraries loaded. Here's how the project
Leiningen (https://github.com/technomancy/leiningen) project.clj file should
look (although you might be able to use more up-to-date versions of the dependencies):
(defproject getting-data "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.6.0"]
[incanter "1.5.5"]])

Downloading the example code


You can download the example code files for all Packt books you have
purchased from your account at http://www.packtpub.com. If you
purchased this book elsewhere, you can visit http://www.packtpub.
com/support and register to have the files e-mailed directly to you.

9
Importing Data for Analysis

Also, in your REPL or your file, include these lines:


(use 'incanter.core
'incanter.io)

Finally, downloaded a list of rest area locations from POI Factory at http://www.poi-
factory.com/node/6643. The data is in a file named data/RestAreasCombined(Ver.
BN).csv. The version designation might be different though, as the file is updated. You'll also
need to register on the site in order to download the data. The file contains this data, which is
the location and description of the rest stops along the highway:
-67.834062,46.141129,"REST AREA-FOLLOW SIGNS SB I-95 MM305","RR, PT,
Pets, HF"
-67.845906,46.138084,"REST AREA-FOLLOW SIGNS NB I-95 MM305","RR, PT,
Pets, HF"
-68.498471,45.659781,"TURNOUT NB I-95 MM249","Scenic Vista-NO
FACILITIES"
-68.534061,45.598464,"REST AREA SB I-95 MM240","RR, PT, Pets, HF"

In the project directory, we have to create a subdirectory named data and place the file in
this subdirectory.

I also created a copy of this file with a row listing the names of the columns and named it
RestAreasCombined(Ver.BN)-headers.csv.

How to do it…
1. Now, use the incanter.io/read-dataset function in your REPL:
user=> (read-dataset "data/RestAreasCombined(Ver.BJ).csv")

| :col0 | :col1 | :col2


| :col3 |
|------------+-----------+--------------------------------------+-
---------------------------|
| -67.834062 | 46.141129 | REST AREA-FOLLOW SIGNS SB I-95 MM305 |
RR, PT, Pets, HF |
| -67.845906 | 46.138084 | REST AREA-FOLLOW SIGNS NB I-95 MM305 |
RR, PT, Pets, HF |
| -68.498471 | 45.659781 | TURNOUT NB I-95 MM249 |
Scenic Vista-NO FACILITIES |
| -68.534061 | 45.598464 | REST AREA SB I-95 MM240 |
RR, PT, Pets, HF |
| -68.539034 | 45.594001 | REST AREA NB I-95 MM240 |
RR, PT, Pets, HF |

10
Chapter 1

2. If we have a header row in the CSV file, then we include :header true in the call to
read-dataset:

user=> (read-dataset "data/RestAreasCombined(Ver.BJ)-headers.csv"


:header true)

| :longitude | :latitude | :name


| :codes |
|------------+-----------+--------------------------------------+-
---------------------------|
| -67.834062 | 46.141129 | REST AREA-FOLLOW SIGNS SB I-95 MM305 |
RR, PT, Pets, HF |
| -67.845906 | 46.138084 | REST AREA-FOLLOW SIGNS NB I-95 MM305 |
RR, PT, Pets, HF |
| -68.498471 | 45.659781 | TURNOUT NB I-95 MM249 |
Scenic Vista-NO FACILITIES |
| -68.534061 | 45.598464 | REST AREA SB I-95 MM240 |
RR, PT, Pets, HF |
| -68.539034 | 45.594001 | REST AREA NB I-95 MM240 |
RR, PT, Pets, HF |

How it works…
Together, Clojure and Incanter make a lot of common tasks easy, which is shown in the How to
do it section of this recipe.

We've taken some external data, in this case from a CSV file, and loaded it into an Incanter
dataset. In Incanter, a dataset is a table, similar to a sheet in a spreadsheet or a database
table. Each column has one field of data, and each row has an observation of data. Some
columns will contain string data (all of the columns in this example did), some will contain
dates, and some will contain numeric data. Incanter tries to automatically detect when a
column contains numeric data and coverts it to a Java int or double. Incanter takes away a
lot of the effort involved with importing data.

There's more…
For more information about Incanter datasets, see Chapter 6, Working with Incanter Datasets.

11
Importing Data for Analysis

Reading JSON data into Incanter datasets


Another data format that's becoming increasingly popular is JavaScript Object Notation
(JSON, http://json.org/). Like CSV, this is a plain text format, so it's easy for programs to
work with. It provides more information about the data than CSV does, but at the cost of being
more verbose. It also allows the data to be structured in more complicated ways, such as
hierarchies or sequences of hierarchies.

Because JSON is a much richer data model than CSV, we might need to transform the
data. In that case, we can just pull out the information we're interested in and flatten the
nested maps before we pass it to Incanter. In this recipe, however, we'll just work with fairly
simple data structures.

Getting ready
First, here are the contents of the Leiningen project.clj file:
(defproject getting-data "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.6.0"]
[incanter "1.5.5"]
[org.clojure/data.json "0.2.5"]])

Use these libraries in your REPL or program (inside an ns form):


(require '[incanter.core :as i]
'[clojure.data.json :as json]
'[clojure.java.io :as io])
(import '[java.io EOFException])

Moreover, you need some data. For this, I have a file named delicious-rss-214k.json
and placed it in the folder named data. It contains a number of top-level JSON objects.
For example, the first one starts like this:
{
"guidislink": false,
"link": "http://designreviver.com/tips/a-collection-of-wordpress-
tutorials-tips-and-themes/",
"title_detail": {
"base": "http://feeds.delicious.com/v2/rss/
recent?min=1&count=100",
"value": "A Collection of Wordpress Tutorials, Tips and Themes
| Design Reviver",
"language": null,
"type": "text/plain"
},
"author": "mccarrd4",

12
Chapter 1

You can download this data file from Infochimps at http://www.ericrochester.com/


clj-data-analysis/data/delicious-rss-214k.json.xz. You'll need to decompress
it into the data directory.

How to do it…
Once everything's in place, we'll need a couple of functions to make it easier to handle the
multiple JSON objects at the top level of the file:

1. We'll need a function that attempts to call a function on an instance of java.


io.Reader and returns nil if there's an EOFException, in case there's a
problem reading the file:
(defn test-eof [reader f]
(try
(f reader)
(catch EOFException e
nil)))

2. Now, we'll build on this to repeatedly parse a JSON document from an instance of
java.io.Reader. We do this by repeatedly calling test-eof until eof or until it
returns nil, accumulating the returned values as we go:
(defn read-all-json [reader]
(loop [accum []]
(if-let [record (test-eof reader json/read)]
(recur (conj accum record))
accum)))

3. Finally, we'll perform the previously mentioned two steps to read the data from
the file:
(def d (i/to-dataset
(with-open
[r (io/reader
"data/delicious-rss-214k.json")]
(read-all-json r))))

This binds d to a new dataset that contains the information read in from the JSON documents.

13
Importing Data for Analysis

How it works…
Similar to all Lisp's (List Processing), Clojure is usually read from the inside out and from
right to left. Let's break it down. clojure.java.io/reader opens the file for reading.
read-all-json parses all of the JSON documents in the file into a sequence. In this case, it
returns a vector of the maps. incanter.core/to-dataset takes a sequence of maps and
returns an Incanter dataset. This dataset will use the keys in the maps as column names, and
it will convert the data values into a matrix. Actually, to-dataset can accept many different
data structures. Try doc to-dataset in the REPL (doc shows the documentation string
attached to the function), or see the Incanter documentation at http://data-sorcery.
org/contents/ for more information.

Reading data from Excel with Incanter


We've seen how Incanter makes a lot of common data-processing tasks very simple, and
reading an Excel spreadsheet is another example of this.

Getting ready
First, make sure that your Leiningen project.clj file contains the right dependencies:
(defproject getting-data "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.6.0"]
[incanter "1.5.5"]"]])

Also, make sure that you've loaded those packages into the REPL or script:
(use 'incanter.core
'incanter.excel)

Find the Excel spreadsheet you want to work on. The file name of my spreadsheet is data/
small-sample-header.xls, as shown in the following screenshot. You can download this
from http://www.ericrochester.com/clj-data-analysis/data/small-sample-
header.xls.

14
Chapter 1

How to do it…
Now, all you need to do is call incanter.excel/read-xls:
user=> (read-xls "data/small-sample-header.xls")

| given-name | surname | relation |


|------------+---------+-------------|
| Gomez | Addams | father |
| Morticia | Addams | mother |
| Pugsley | Addams | brother |

How it works…
This can read standard Excel files (.xls) and the XML-based file format introduced in
Excel 2003 (.xlsx).

Reading data from JDBC databases


Reading data from a relational database is only slightly more complicated than reading from
Excel, and much of the extra complication involves connecting to the database.

Fortunately, there's a Clojure-contributed package that sits on top of JDBC (the Java database
connector API, http://www.oracle.com/technetwork/java/javase/jdbc/index.
html) and makes working with databases much easier. In this example, we'll load a table
from an SQLite database (http://www.sqlite.org/), which stores the database in a
single file.

Getting ready
First, list the dependencies in your Leiningen project.clj file. We will also need to include
the database driver library. For this example, it is org.xerial/sqlite-jdbc:
(defproject getting-data "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.6.0"]
[incanter "1.5.5"]
[org.clojure/java.jdbc "0.3.3"]
[org.xerial/sqlite-jdbc "3.7.15-M1"]])

Then, load the modules into your REPL or script file:


(require '[incanter.core :as i]
'[clojure.java.jdbc :as j])

15
Importing Data for Analysis

Finally, get the database connection information. I have my data in an SQLite database file
named data/small-sample.sqlite, as shown in the following screenshot. You can
download this from http://www.ericrochester.com/clj-data-analysis/data/
small-sample.sqlite.

How to do it…
Loading the data is not complicated, but we'll make it easier with a wrapper function:

1. We'll create a function that takes a database connection map and a table name and
returns a dataset created from this table:
(defn load-table-data
"This loads the data from a database table."
[db table-name]
(i/to-dataset
(j/query db (str "SELECT * FROM " table-name ";"))))

16
Chapter 1

2. Next, we define a database map with the connection parameters suitable for
our database:
(defdb {:subprotocol "sqlite"
:subname "data/small-sample.sqlite"
:classname "org.sqlite.JDBC"})

3. Finally, call load-table-data with db and a table name as a symbol or string:


user=> (load-table-data db 'people)

| :relation | :surname | :given_name |


|-------------+----------+-------------|
| father | Addams | Gomez |
| mother | Addams | Morticia |
| brother | Addams | Pugsley |||

How it works…
The load-table-data function passes the database connection information directly
through to clojure.java.jdbc/query.query. It creates an SQL query that returns all
of the fields in the table that is passed in. Each row of the result is a sequence of hashes
mapping column names to data values. This sequence is wrapped in a dataset by
incanter.core/to-dataset.

See also
Connecting to different database systems using JDBC isn't necessarily a difficult task,
but it's dependent on which database you wish to connect to. Oracle has a tutorial for
how to work with JDBC at http://docs.oracle.com/javase/tutorial/jdbc/
basics, and the documentation for the clojure.java.jdbc library has some good
information too (http://clojure.github.com/java.jdbc/). If you're trying to find
out what the connection string looks like for a database system, there are lists available
online. The list at http://www.java2s.com/Tutorial/Java/0340__Database/
AListofJDBCDriversconnectionstringdrivername.htm includes the major drivers.

17
Importing Data for Analysis

Reading XML data into Incanter datasets


One of the most popular formats for data is XML. Some people love it, while some hate it.
However, almost everyone has to deal with it at some point. While Clojure can use Java's
XML libraries, it also has its own package which provides a more natural way to work with
XML in Clojure.

Getting ready
First, include these dependencies in your Leiningen project.clj file:
(defproject getting-data "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.6.0"]
[incanter "1.5.5"]])

Use these libraries in your REPL or program:


(require '[incanter.core :as i]
'[clojure.xml :as xml]
'[clojure.zip :as zip])

Then, find a data file. I visited the website for the Open Data Catalog for Washington, D.C.
(http://data.octo.dc.gov/), and downloaded the data for the 2013 crime incidents.
I moved this file to data/crime_incidents_2013_plain.xml. This is how the contents
of the file look:
<?xml version="1.0" encoding="iso-8859-1"?>
<dcst:ReportedCrimes
xmlns:dcst="http://dc.gov/dcstat/types/1.0/">
<dcst:ReportedCrime
xmlns:dcst="http://dc.gov/dcstat/types/1.0/">
<dcst:ccn><![CDATA[04104147]]></dcst:ccn>
<dcst:reportdatetime>
2013-04-16T00:00:00-04:00
</dcst:reportdatetime>

18
Chapter 1

How to do it…
Now, let's see how to load this file into an Incanter dataset:

1. The solution for this recipe is a little more complicated, so we'll wrap it into a function:
(defn load-xml-data [xml-file first-data next-data]
(let [data-map (fn [node]
[(:tag node) (first (:content node))])]
(->>
(xml/parse xml-file)
zip/xml-zip
first-data
(iterate next-data)
(take-while #(not (nil? %))
(map zip/children)
(map #(mapcat data-map %))
(map #(apply array-map %))
i/to-dataset)))

2. We can call the function like this. Because there are so many columns, we'll just
verify the data that is loaded by looking at the column names and the row count:
user=> (def d
(load-xml-data "data/crime_incidents_2013_plain.xml"
zip/down zip/right))
user=> (i/col-names d)
[:dcst:ccn :dcst:reportdatetime :dcst:shift :dcst:offense
:dcst:method :dcst:lastmodifieddate :dcst:blocksiteaddress
:dcst:blockxcoord :dcst:blockycoord :dcst:ward :dcst:anc
:dcst:district :dcst:psa :dcst:neighborhoodcluster :dcst:busi
nessimprovementdistrict :dcst:block_group :dcst:census_tract
:dcst:voting_precinct :dcst:start_date :dcst:end_date]
user=> (i/nrow d)
35826

This looks good. This gives you the number of crimes reported in the dataset.

How it works…
This recipe follows a typical pipeline for working with XML:

1. Parsing an XML data file


2. Extracting the data nodes
3. Converting the data nodes into a sequence of maps representing the data
4. Converting the data into an Incanter dataset

19
Importing Data for Analysis

load-xml-data implements this process. This takes three parameters:

ff The input filename


ff A function that takes the root node of the parsed XML and returns the first data node
ff A function that takes a data node and returns the next data node or nil, if there are
no more nodes

First, the function parses the XML file and wraps it in a zipper (we'll talk more about zippers in
the next section). Then, it uses the two functions that are passed in to extract all of the data
nodes as a sequence. For each data node, the function retrieves that node's child nodes and
converts them into a series of tag name / content pairs. The pairs for each data node are
converted into a map, and the sequence of maps is converted into an Incanter dataset.

There's more…
We used a couple of interesting data structures or constructs in this recipe. Both are common
in functional programming or Lisp, but neither have made their way into more mainstream
programming. We should spend a minute with them.

Navigating structures with zippers


The first thing that happens to the parsed XML is that it gets passed to clojure.zip/
xml-zip. Zippers are standard data structures that encapsulate the data at a position in a
tree structure, as well as the information necessary to navigate back out. This takes Clojure's
native XML data structure and turns it into something that can be navigated quickly using
commands such as clojure.zip/down and clojure.zip/right. Being a functional
programming language, Clojure encourages you to use immutable data structures, and
zippers provide an efficient, natural way to navigate and modify a tree-like structure, such as
an XML document.

Zippers are very useful and interesting, and understanding them can help you understand
and work better with immutable data structures. For more information on zippers, the
Clojure-doc page is helpful (http://clojure-doc.org/articles/tutorials/
parsing_xml_with_zippers.html). However, if you would rather dive into the deep
end, see Gerard Huet's paper, The Zipper (http://www.st.cs.uni-saarland.de/edu/
seminare/2005/advanced-fp/docs/huet-zipper.pdf).

Processing in a pipeline
We used the ->> macro to express our process as a pipeline. For deeply nested function calls,
this macro lets you read it from the left-hand side to the right-hand side, and this makes the
process's data flow and series of transformations much more clear.

20
Chapter 1

We can do this in Clojure because of its macro system. ->> simply rewrites the calls into
Clojure's native, nested format as the form is read. The first parameter of the macro is
inserted into the next expression as the last parameter. This structure is inserted into the third
expression as the last parameter, and so on, until the end of the form. Let's trace this through
a few steps. Say, we start off with the expression (->> x first (map length) (apply
+)). As Clojure builds the final expression, here's each intermediate step (the elements to be
combined are highlighted at each stage):

1. (->> x first (map length) (apply +))


2. (->>(first x) (map length) (apply +))
3. (->>(map length (first x)) (apply +))
4. (apply + (map length (first x)))

Comparing XML and JSON


XML and JSON (from the Reading JSON data into Incanter datasets recipe) are very similar.
Arguably, much of the popularity of JSON is driven by disillusionment with XML's verboseness.

When we're dealing with these formats in Clojure, the biggest difference is that JSON is
converted directly to native Clojure data structures that mirror the data, such as maps and
vectors Meanwhile, XML is read into record types that reflect the structure of XML, not the
structure of the data.

In other words, the keys of the maps for JSON will come from the domains, first_name or
age, for instance. However, the keys of the maps for XML will come from the data format, such
as tag, attribute, or children, and the tag and attribute names will come from the domain.
This extra level of abstraction makes XML more unwieldy.

Scraping data from tables in web pages


There's data everywhere on the Internet. Unfortunately, a lot of it is difficult to reach. It's
buried in tables, articles, or deeply nested div tags. Web scraping (writing a program that
walks over a web page and extracts data from it) is brittle and laborious, but it's often the only
way to free this data so it can be used in our analyses. This recipe describes how to load a
web page and dig down into its contents so that you can pull the data out.

To do this, we're going to use the Enlive (https://github.com/cgrand/enlive/wiki)


library. This uses a domain specific language (DSL, a set of commands that make a small set
of tasks very easy and natural) based on CSS selectors to locate elements within a web page.
This library can also be used for templating. In this case, we'll just use it to get data back out
of a web page.

21
Importing Data for Analysis

Getting ready
First, you have to add Enlive to the dependencies in the project.clj file:
(defproject getting-data "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.6.0"]
[incanter "1.5.5"]
[enlive "1.1.5"]])

Next, use these packages in your REPL or script:


(require '[clojure.string :as string]
'[net.cgrand.enlive-html :as html]
'[incanter.core :as i])
(import [java.net URL])

Finally, identify the file to scrape the data from. I've put up a file at http://www.
ericrochester.com/clj-data-analysis/data/small-sample-table.html,
which looks like this:

It's intentionally stripped down, and it makes use of tables for layout (hence the comment
about 1999).

22
Chapter 1

How to do it…
1. Since this task is a little complicated, let's pull out the steps into several functions:
(defn to-keyword
"This takes a string and returns a normalized keyword."
[input]
(->input
string/lower-case
(string/replace \space \-)
keyword))

(defn load-data
"This loads the data from a table at a URL."
[url]
(let [page (html/html-resource (URL. url))
table (html/select page [:table#data])
headers (->>
(html/select table [:tr :th])
(map html/text)
(map to-keyword)
vec)
rows (->> (html/select table [:tr])
(map #(html/select % [:td]))
(map #(map html/text %))
(filterseq))]
(i/dataset headers rows))))))

2. Now, call load-data with the URL you want to load data from:
user=> (load-data (str "http://www.ericrochester.com/"
"clj-data-analysis/data/small-sample-table.html"))
| :given-name | :surname | :relation |
|-------------+----------+-------------|
| Gomez | Addams | father |
| Morticia | Addams | mother |
| Pugsley | Addams | brother |
| Wednesday | Addams | sister |

23
Importing Data for Analysis

How it works…
The let bindings in load-data tell the story here. Let's talk about them one by one.

The first binding has Enlive download the resource and parse it into Enlive's internal
representation:
(let [page (html/html-resource (URL. url))

The next binding selects the table with the data ID:
table (html/select page [:table#data])

Now, select of all the header cells from the table, extract the text from them, convert each to a
keyword, and then convert the entire sequence into a vector. This gives headers for the dataset:
headers (->>
(html/select table [:tr :th])
(map html/text)
(map to-keyword)
vec)

First, select each row individually. The next two steps are wrapped in map so that the cells in
each row stay grouped together. In these steps, select the data cells in each row and extract
the text from each. Last, use filterseq, which removes any rows with no data, such as the
header row:
rows (->> (html/select table [:tr])
(map #(html/select % [:td]))
(map #(map html/text %))
(filterseq))]

Here's another view of this data. In this image, you can see some of the code from this web
page. The variable names and select expressions are placed beside the HTML structures that
they match. Hopefully, this makes it more clear how the select expressions correspond to the
HTML elements:

24
Chapter 1

Finally, convert everything to a dataset. incanter.core/dataset is a lower level


constructor than incanter.core/to-dataset. It requires you to pass in the column
names and data matrix as separate sequences:
(i/dataset headers rows)))

It's important to realize that the code, as presented here, is the result of a lot of trial and error.
Screen scraping usually is. Generally, I download the page and save it, so I don't have to keep
requesting it from the web server. Next, I start the REPL and parse the web page there. Then,
I can take a look at the web page and HTML with the browser's view source function, and I can
examine the data from the web page interactively in the REPL. While working, I copy and paste
the code back and forth between the REPL and my text editor, as it's convenient. This workflow
and environment (sometimes called REPL-driven-development) makes screen scraping
(a fiddly, difficult task at the best of times) almost enjoyable.

See also
ff The next recipe, Scraping textual data from web pages, has a more involved example
of data scraping on an HTML page
ff The Aggregating data from different formats recipe has a practical, real-life example
of data scraping in a table

Scraping textual data from web pages


Not all of the data on the Web is in tables, as in our last recipe. In general, the process
to access this nontabular data might be more complicated, depending on how the page
is structured.

Getting ready
First, we'll use the same dependencies and the require statements as we did in the last
recipe, Scraping data from tables in web pages.

Next, we'll identify the file to scrape the data from. I've put up a file at http://www.
ericrochester.com/clj-data-analysis/data/small-sample-list.html.

This is a much more modern example of a web page. Instead of using tables, it marks up the
text with the section and article tags and other features from HTML5, which help convey
what the text means, not just how it should look.

25
Importing Data for Analysis

As the screenshot shows, this page contains a list of sections, and each section contains a list
of characters:

How to do it…
1. Since this is more complicated, we'll break the task down into a set of
smaller functions:
(defn get-family
"This takes an article element and returns the family
name."
[article]
(string/join
(map html/text (html/select article [:header :h2]))))

26
Chapter 1
(defn get-person
"This takes a list item and returns a map of the person's
name and relationship."
[li]
(let [[{pnames :content} rel] (:content li)]
{:name (apply str pnames)
:relationship (string/trim rel)}))

(defn get-rows
"This takes an article and returns the person mappings,
with the family name added."
[article]
(let [family (get-family article)]
(map #(assoc % :family family)
(map get-person
(html/select article [:ul :li])))))

(defn load-data
"This downloads the HTML page and pulls the data out of
it."
[html-url]
(let [html (html/html-resource (URL. html-url))
articles (html/select html [:article])]
(i/to-dataset (mapcat get-rows articles))))

2. Now that these functions are defined, we just call load-data with the URL that we
want to scrape:
user=> (load-data (str "http://www.ericrochester.com/"
"clj-data-analysis/data/"
"small-sample-list.html"))
| :family | :name | :relationship |
|----------------+-----------------+---------------|
| Addam's Family | Gomez Addams | — father |
| Addam's Family | Morticia Addams | — mother |
| Addam's Family | Pugsley Addams | — brother |

27
Importing Data for Analysis

How it works…
After examining the web page, each family is wrapped in an article tag that contains a
header with an h2 tag. get-family pulls that tag out and returns its text.

get-person processes each person. The people in each family are in an unordered list
(ul), and each person is in an li tag. The person's name itself is in an em tag. let gets the
contents of the li tag and decomposes it in order to pull out the name and relationship
strings. get-person puts both pieces of information into a map and returns it.

get-rows processes each article tag. It calls get-family to get that information from
the header, gets the list item for each person, calls get-person on the list item, and adds
the family to each person's mapping.

Here's how the HTML structures correspond to the functions that process them. Each function
name is mentioned beside the elements it parses:

Finally, load-data ties the process together by downloading and parsing the HTML file and
pulling the article tags from it. It then calls get-rows to create the data mappings and
converts the output to a dataset.

28
Chapter 1

Reading RDF data


More and more data is going up on the Internet using linked data in a variety of formats
such as microformats, RDFa, and RDF/XML.

Linked data represents entities as consistent URLs and includes links to other databases
of the linked data. In a sense, it's the computer equivalent of human-readable web
pages. Often, these formats are used for open data, such as the data published by some
governments, like in the UK and elsewhere.

Linked data adds a lot of flexibility and power, but it also introduces more complexity.
Often, to work effectively with linked data, we need to start a triple store of some kind.
In this recipe and the next three, we'll use Sesame (http://rdf4j.org/) and the
kr Clojure library (https://github.com/drlivingston/kr).

Getting ready
First, we need to make sure that the dependencies are listed in our Leiningen
project.clj file:

(defproject getting-data "0.1.0-SNAPSHOT"


:dependencies [[org.clojure/clojure "1.6.0"]
[incanter "1.5.5"]
[edu.ucdenver.ccp/kr-sesame-core "1.4.17"]
[org.clojure/tools.logging "0.3.0"]
[org.slf4j/slf4j-simple "1.7.7"]])

We'll execute these packages to have these loaded into our script or REPL:
(use 'incanter.core
'edu.ucdenver.ccp.kr.kb
'edu.ucdenver.ccp.kr.rdf
'edu.ucdenver.ccp.kr.sparql
'edu.ucdenver.ccp.kr.sesame.kb
'clojure.set)
(import [java.io File])

For this example, we'll get data from the Telegraphis Linked Data assets. We'll pull down the
database of currencies at http://telegraphis.net/data/currencies/currencies.
ttl. Just to be safe, I've downloaded that file and saved it as data/currencies.ttl, and
we'll access it from there.

We'll store the data, at least temporarily, in a Sesame data store (http://notes.3kbo.
com/sesame) that allows us to easily store and query linked data.

29
Importing Data for Analysis

How to do it…
The longest part of this process will be to define the data. The libraries we're using do all of
the heavy lifting, as shown in the steps given below:

1. First, we will create the triple store and register the namespaces that the data uses.
We'll bind this triple store to the name tstore:
(defn kb-memstore
"This creates a Sesame triple store in memory."
[]
(kb :sesame-mem))
(defn init-kb [kb-store]
(register-namespaces
kb-store
'(("geographis"
"http://telegraphis.net/ontology/geography/geography#")
("code"
"http://telegraphis.net/ontology/measurement/code#")
("money"
"http://telegraphis.net/ontology/money/money#")
("owl"
"http://www.w3.org/2002/07/owl#")
("rdf"
"http://www.w3.org/1999/02/22-rdf-syntax-ns#")
("xsd"
"http://www.w3.org/2001/XMLSchema#")
("currency"
"http://telegraphis.net/data/currencies/")
("dbpedia" "http://dbpedia.org/resource/")
("dbpedia-ont" "http://dbpedia.org/ontology/")
("dbpedia-prop" "http://dbpedia.org/property/")
("err" "http://ericrochester.com/"))))

(def t-store (init-kb (kb-memstore)))

30
Other documents randomly have
different content
Elle me prit entre ses larges bras
et m’emporta en courant, le visage
serré contre sa poitrine de déesse ou
de phénomène de foire.
Etrange famille qui va finir avec
moi!
Ma mère était une mince et
délicate jeune femme, toujours
malade, et mon grand-père
d’Herbaupair était un petit homme
falot et chétif, qui n’avait eu dans sa
vie qu’une passion: celle des antiquités.
C’est lui qui rassembla tout ce que la pluie, l’humidité et plus de trente
ans d’abandon détruisirent à la Tremblée.
Je l’aperçus une seule fois et il ne prit point garde à moi. Il ne
s’intéressait qu’aux enfants peints par Boilly.
Il portait un costume assez bizarre et il jouait perpétuellement avec une
grosse loupe dont j’avais bien envie...
*
* *
J’ai déniché derrière une porte une peinture qui représente une vue
ocreuse de Rome.
Devant cette vision noble et glorieuse, cette terre fauve que ne
déshonorent aucun pâturage, aucun bétail à l’engrais, devant les arcs ruinés
et les aqueducs écroulés, je songe à l’épouvantable ennui que m’infligeait
tout ce qui touchait à Rome, au temps où j’achevais mes classes, au collège.
Les vertus civiques et militaires de ses grands hommes, leurs mots
historiques, leurs pompeuses attitudes me glaçaient. Je tenais les Romains
pour un peuple de bavards, de faiseurs de routes et de lois, et Auguste me
semblait le personnage le plus ridicule et le plus pompier de l’Histoire.
Combien me plaisait davantage ce que j’appelais l’opposition orientale à la
République et à l’Empire!
J’aimais les princes efféminés qu’allait vaincre facilement quelque
militaire de carrière, aux joues et aux lèvres bleuies par le rasoir; les
princesses étranges qui regrettaient Ecbatane ou Césarée, la Bactriane et la
Cappadoce, dans la ville capitale; ces belles barbares qui troublaient les
césars et les proconsuls avec leur teint de fellahines et leurs parfums
inconnus. Mais j’étais naturellement un mauvais élève, puisque je
n’admirais pas ces juges de paix, ces agents voyers et ces briscards
coloniaux...
*
* *
Je voudrais connaître mes nouveaux compagnons, les arbres qui
m’entourent, et je ne sais le nom d’aucun.
D’ailleurs, ils ont poussé si drus, si mêlés les uns aux autres qu’ils ne
forment plus qu’une foule végétale.
Je veux tout de même me familiariser avec eux...

Ce matin, bien avant l’aube, j’ai dû fuir le lit où je n’avais pas dormi et
aller dans le parc.
Je suis de plus en plus sollicité par le côté mystérieux et obscur du
monde.
La terre avait le réveil pénible des hommes qui remontent lentement des
gouffres du sommeil et du songe, des abîmes de la nuit.
Elle avait l’air d’hésiter, il me semblait qu’elle allait lâcher un secret.
L’aile fermée que chacun porte en soi allait-elle se déployer en moi?
Mais non, chaque chose a repris sa place, le soleil s’est levé, et cette
inquiétude infinie n’était que dans mon cœur...
*
* *
Un grand oiseau de mer, venant on ne sait d’où, a traversé ce soir, vers
cinq heures, le ciel d’été, orange et mauve.
Il allait, le cou tendu, sans un frémissement de plumes dans l’azur tiède,
comme une grande chose soyeuse et bien lancée.
J’ai entendu le bruit d’un coup de fusil.
Tout le monde doit en parler au village, comme si l’homme qui saigne
les porcs, le tailleur bossu, l’épicière, l’aubergiste, les vieux qui tettent des
pipes vides et les vieilles qui se chauffent au soleil avaient vu le ciel se
creuser et aperçu, dans une échappée vermeille, le passage d’un être
surnaturel...
*
* *
J’ai retrouvé des vers que j’écrivis en Champagne, pendant la guerre, un
soir inhumain que je songeais au tableau de Bœcklin: L’Ile des Morts.
La contrée la plus tragique et la plus désolée du monde était en avant de
Somme-Suippe; c’était un aride paysage calcaire et minéral. Tout y était
pâle de la pâleur mortuaire des craies.
C’est seulement au versant des astres éteints et des globes morts qu’on
eût pu trouver ces lividités de sel et de plâtre.
Ce coin du front était nettoyé comme un os, ce secteur blanc,
squelettique et spectral avait l’air d’être sous un suaire. Je m’aperçois que
ce poème maladroit est tout en rimes féminines. Cela ne me déplaît pas. Les
rimes sourdes confèrent aux strophes une pesanteur étrange, mais voici
cette poésie à laquelle j’ai donné le nom du tableau:
L’ILE DES MORTS
Des cyprès, des rocs blancs hors du monde... C’est l’Ile
Où vivent les grands morts quand ils quittent la terre;
Un crépuscule doux, vaporeux et tranquille
Y répand ses clartés de perle et son mystère.

Son bois sacré de pins, de lauriers métalliques


Semble attendre toujours de pures chasseresses,
Et dans le bleu divin des soirs mélancoliques
On dirait que, toujours, vont passer des druidesses.

Victor Hugo, vêtu de la cape marine


Qu’il portait à Jersey, poursuit un vaste rêve...
Lorsque sort Beethoven, Musset et Lamartine
Saluent, et le martyr de la musique lève

Son énorme chapeau trop enfoncé... Banville


Au jardin de Ronsard cueille des roses blanches
Et des œillets qu’il veut offrir au bon Virgile.
Lord Byron à Chénier dit des vers sous les branches...

Hésiode et Gautier ont des barbes pareilles;


Shakspeare et Rabelais dans l’herbe rient ensemble;
Dante, toujours coiffé de capuces vermeilles,
Sur le bras de Balzac pose sa main qui tremble.

Ils sont là tous, dans l’île aux lumières sereines.


Aucun souffle n’émeut les arbres du rivage,
Les heures ne fuient pas et sont élyséennes.
Rarement une barque aborde sur la plage.

C’est l’asile où tout n’est que paix harmonieuse,


Où la table toujours est mise sous les roses
Des rosiers aussi hauts que le pin et l’yeuse;
C’est l’Ile du silence et des apothéoses.

Mais quand passent, venant d’une affreuse bataille,


Dans un lourd battement d’aile vierge et meurtrie
Des âmes de soldats, courbant leur grande taille,
Gœthe et Schiller, pensifs, maudissent leur patrie...

*
* *
Je passe aujourd’hui l’après-midi allongé sur mon lit, à regarder le ciel
au-dessus des arbres.
Au plus profond, au plus intime de moi, dans ces régions intérieures où
ne peut vivre aucun mensonge, il n’y a peut-être que le désir d’en avoir fini
vite avec les misères que je traîne, et je me sens soulevé par l’espoir des
grandes migrations inconnues.
Tout arrivera sans doute comme je l’imagine.
Un matin ou un soir, lorsque Jean entrera, il me trouvera à cette place,
immobile et couché, tel que je le suis maintenant.
Il constatera que je suis mort.
Mort!... savent-ils ce qu’ils disent, ceux qui prononcent ce mot?
Invisible, mon âme flottera au-dessus de tout ce qu’elle aura laissé et,
après quelques formalités qui ne me regarderont plus, quand on aura fait
disparaître ce... Comment dire?... Cet amas de phosphate et de matières
ammoniacales, elle prendra son vol vers les blancs et bleus paysages
fugitifs et changeants, que je contemple de ma croisée.
Immense ivresse des affranchissements!
Je parcourrai le ciel, je m’engagerai dans ces ravins d’ombre effilochée
au penchant des collines neigeuses que composent les nuages; j’escaladerai
des pics et des falaises d’écume, des glaciers brumeux; je traverserai de
vaporeux défilés pour gagner des champs de neige tiède, des moissons
floconneuses. Je serai submergé par des marées, j’assisterai à des débâcles
de nuées que je verrai fondre comme des blocs polaires, et je partirai vers
les régions supérieures où n’atteignent pas les oiseaux,... puis... puis... je ne
sais plus ce qui arrivera... Mais je passe cet après-midi dans les nuages,
l’esprit presque délivré...
*
* *
Je ne suis tout de même pas assez loin du village.
Il me semble, quand je veille, que je l’entends dormir. Une étoile se noie
dans l’abreuvoir et la lune est derrière le clocher trapu et sans idéalisme de
sa petite église romane.
Je l’imagine cette nuit et l’humble bourgade abrite toutes les situations
éternisées par l’art des écrivains.
Sous un ciel nocturne, dont la pureté religieuse fait songer à un grand
vers bleu sombre de Virgile, autour de cette place provinciale pareille à
celles où Coppée fit rêver de poétiques receveurs de l’enregistrement, un
héros ou une héroïne littéraires habitent dans chaque maison.
Ici, vit le Père Goriot, de Balzac; là, Eugénie Grandet range le linge
qu’elle a elle-même lavé, tandis que l’avare Grandet recompte ses billets.
Derrière le géranium de telle croisée, relisant une lettre de Vincent, il y a
Mireille, blanche de la blancheur ardente des camélias. Léon, le clerc de
notaire qui passe dans le roman de Flaubert, songe à Paris, aux actrices, aux
salons, en parcourant des échos mondains dans un journal. Emma Bovary
tourne le dos à son mari qui semble tirer, en dormant, sur le tuyau d’une
invisible pipe car il dort, avec cette croupe chaude à portée de sa main...
Dans des chambres obscures ronflent les paysans de Zola. Un vieux, dont
on convoite l’héritage, est secoué par une crise d’asthme pendant que son
fils, qui préférerait dormir, est obligé de besogner sa grosse femme qui fait
craquer le lit, sans se soucier de son beau-père ni de son dernier né qui
braille, travaillé par la dentition ou la colique. Et les cochons grognent dans
les étables, et les rats volent du lard dans les buffets. Quelle farce obscure et
monotone emportée autour du soleil à une vitesse de quatre cent douze
lieues par minute!...
*
* *
Le vent a emporté un journal dans le
parc.
Sa première page était étalée bien à plat,
sur l’herbe. Je n’avais qu’à me pencher
pour lire et je ne l’ai pas fait.
Que m’aurait-il appris?
Je sais ce qu’il contenait sans l’avoir
regardé: on doit toujours se battre en
Orient, et la famine et le choléra occupent
sérieusement les armées rouges. Des
garçons sans scrupules ont volé, dans un
rapide, les bijoux des grosses dames qui
vont si souvent aux cabinets. On a entôlé un rentier qui avait eu la faiblesse
de suivre dans un garni deux filles, dont les mollets polissons n’étaient pas
à comparer à ceux de sa digne épouse, et cela ainsi jusqu’aux rébus de la
quatrième page proposés à des œdipes qui gagnent un stylographe ou une
fiole de parfum chimique.

J’en ai fait une boule que j’ai lancée par-dessus le mur... Un train sifflait
au loin...
*
* *
Il y a plus d’un mois que je n’avais ouvert ce carnet. Les gaz que le
docteur Faust fabriquait chez Mᵐᵉ Bertha Krupp agissent de mieux en
mieux. Chose curieuse, à mesure que mes forces déclinent et que le mal me
gagne, je suis de plus en plus poursuivi par des images de femmes.
Je ne peux pourtant pas sortir, dans l’état où je suis, et séduire quelque
fille du village. La plus minable me rirait au nez.
*
* *
Gustave. Poste restante. B. 21. Hôtel de Ville. Envoi discret de
catalogues... J’ai lu cette adresse, au hasard, avant de quitter Paris, et je ne
sais pourquoi elle me hante à la façon d’un leit-motiv.
Je viens d’écrire à ce commerçant discret. Il expédie des paquets de
tissus caoutchoutés qui deviennent, quand on les gonfle, de véritables
femmes. Ce sont les seules qui puissent me convenir. On m’a affirmé que
des explorateurs et certains solitaires n’en souhaitent pas d’autres.
J’ai coupé toutes les ficelles qui me rattachaient au monde; si quelqu’un
parlait de moi aux gens du village, on lui dirait que je suis fou. C’est peut-
être vrai. Pourquoi n’aimerais-je pas une grande poupée?... Hé... pas si
grande... j’ai donné les mesures. Je suis de l’avis de Michelet. Il faut que sa
tête arrive à la hauteur de mon cœur...
*
* *
Lorsqu’on frappera à la porte verte, ce sera Elle!
Je ne pense plus qu’à son arrivée. J’ai commandé un petit trousseau: des
bas de soie, une chemise, un peignoir, un bonnet de dentelles, un flacon
d’essence de rose, un autre de musc; mais j’ignore tout de cette inconnue, et
comment l’appellerai-je? Je vais songer à un nom.
*
* *
Je ne crois pas trouver.
On peut étiqueter, une fois pour toutes, les choses immobiles. Elles ne
changent jamais. Le nom qu’on leur donne les désigne toujours. Mais les
femmes!...
J’en ai connu une qui s’appelait Marie. Cela lui allait parfaitement
jusqu’à midi. Ses cheveux châtains, mouillés et lissés au sortir du bain, en
faisaient une grasse et bourgeoise madone. Elle avait des réveils enfantins et
sa toilette était pudique et secrète.
Le déjeuner troublait légèrement toutes ces candeurs.
Après un verre de vieux bordeaux et un doigt de chartreuse, elle
s’appelait Sapho, Lucrèce, Mercédès ou Rosa.
Le prénom d’une femme, qui prend son café au lait ou qui brode en
compagnie de sa mère, ne lui convient plus le soir, quand elle est nue.
Elle s’appelle Marthe, Thérèse ou Monique, et cela est très bien ainsi.
Elle coud, elle suce le bout de son doigt où une piqûre d’aiguille a fait
brusquement éclore une petite coccinelle de corail sombre; elle
confectionne une tarte devant le fourneau, elle lit un roman honnête, et elle
peut porter le nom qu’elle a reçu.
Si elle met sur ses cheveux un grand chapeau de soleil et qu’elle aille
dans le jardin, elle s’évade déjà. Elle doit s’appeler Charlotte, Isabelle, ou
Rosine. Charlotte, c’est comme un abricot plein de taches de rousseur, et si
Rosine est un prénom enveloppé dans une large feuille de rose rose, Isabelle
a le blanc crème des gloires de Dijon.
La nuit est venue. Elle est seule avec son mari et elle pousse le verrou de
la porte, toute pareille à ces amantes potelées et vermeilles qui font le même
geste dans les estampes galantes du XVIIIᵉ siècle. Un sein gonflé s’échappe
hors de son corsage, un de ses bas tombe sur sa jambe ronde. Elle est alors
Rosette ou Fanchon...
Le voici en chemise, avec ses mules de satin bleu, les bras arrondis, les
mains à son chignon qu’elle tord. Elle est devenue la gaillarde bourgeoise
des contes italiens qui va prendre son plaisir avec un beau capitaine ou un
jeune capucin paillard qu’elle a gavé d’oie rôtie et de vin vieux...
Elle jette ses pantoufles minuscules et ses derniers voiles, et, sans un
peigne, sans une bague, elle est une femme des premiers âges du monde,
elle est Laïs ou Phryné, Atalante, Chloé, Amaryllis... mais aucun de ces
noms ne lui convient longtemps et, quand elle s’endort sur le bras qui l’a
étreinte, elle redevient presque la petite fille alourdie de sommeil qu’on
appelait Moune, Ninette ou Lili...
*
* *
Au fond, il n’y a rien de très cocasse dans le désir que j’ai de cette
poupée.
Mon grand’père d’Herbaupair, après avoir fait deux enfants à sa femme,
l’abandonna à la Tremblée et n’aima plus que les visages et les corps peints
sur des toiles. Je l’imagine dans une rue de Paris, vers 1860. Il était
absolument normal.
Devant ou derrière lui, sur le trottoir mouillé de pluie, un homme de son
âge suivait une lorette ou une modiste qui jouait de la croupe et soulevait sa
jupe sur de gros jarrets qui tendaient ses bas blancs.
Il obtenait un rendez-vous pour le soir, se ruinait en vespetro et en
marasquin pour régaler la belle qui finissait par se laisser conduire à l’hôtel.
Les draps y étaient douteux et humides; il gelait dans la chambre

inhospitalière; la fille, qui montrait soudain une rapacité sordide de


commerçante, tarifait ses charmes douteux et ses caresses, et le galantin
dégrisé ne songeait qu’à fuir, et il faisait le simulacre de l’amour, honteux
comme tous les simulacres, en écoutant les

vidangeurs, seuls maîtres de la rue à cette heure déserte et noire. Mon


grand-père, lui, se rendait tranquillement à de mystérieux rendez-vous chez
les brocanteurs auvergnats, cherchant les seules femmes qu’il aimât: les
nymphes de Fragonard, les laitières de Greuze et les belles dames poudrées
des anciens pastels... Ses amours étaient les plus belles... Il se ruina presque
cependant pour une fille rencontrée à la terrasse de Tortoni...
*
* *
Elle devrait être ici.
Je suis de plus en plus nerveux, depuis que je l’attends. La moindre
chose m’irrite, et j’ai failli avoir une épouvantable crise pour avoir vu un
crapaud. Sa hideur, ses pustules ne m’ont pas trop répugné, c’est son
attitude qui m’a rendu furieux.
Ce crapaud, que mon domestique protège, est une sorte de divinité
bouddhique, ventrue, molle et grenue; il allait lent, solennel, important,
ridicule, et je comprenais qu’il se savait sacré. Il avait la majesté pompeuse
et bête des dieux auxquels il est interdit de toucher; la suffisance des gens
en place; l’orgueil tranquille et béat de ceux qui se croient indispensables,
quelque chose de prudhommesque et de despotique, et, alors, j’ai eu
brusquement envie de lui prouver à coups de trique, à coups de pierre, que
tout ce dont il était si fier ne tenait pas debout, que ses occupations d’aide
jardinier et de garde champêtre n’étaient pas plus sérieuses que celles des
araignées, des limaces et des rats, et qu’il n’avait pas le droit d’avoir une
attitude aussi grotesque, et qu’il n’était qu’un crapaud, un sale crapaud dans
le parc d’un homme en train de mourir.
J’en ai été secoué toute la journée...
*
* *
J’ai prié mon domestique de différer aujourd’hui son voyage à la ville où
il va faire des achats.
Je crois qu’elle ne tardera pas à arriver et je ne veux pas être obligé, moi-
même, d’ouvrir la porte et de voir le facteur.
Il ira un autre jour, quoique ces voyages,—je le devine,—l’enchantent.
Je la connais, cette sous-préfecture! Des courtiers en vins boivent de la
bière à la terrasse du café d’Orient; les jeunes filles d’un pensionnat sortent
pour la promenade; une jeune femme, chaussée de blanc et coiffée d’une
charlotte de mousseline, descend la grand’rue. Elle s’arrête chez le pâtissier
en renom.
Sous une gaze jaune, qui les défend contre les mouches, des babas ivres
de rhum sucré défaillent dans des assiettes à filets dorés... La jeune femme
sort, saluée par un vieux roquentin vêtu de flanelle bleue à rayures, un
avocat dont les aventures et l’éloquence sont célèbres jusqu’au chef-lieu.
C’est dans cette rue déserte où j’ai passé, il y a plus de vingt ans, que
Jean fait ses emplettes, puis il boit un bock ou un apéritif près de la gare,
seul comme un vieux comique lugubre de l’Eden-Café, qui est le concert le
plus couru de l’endroit.
L’Eden-Café! J’y ai connu l’amour pour la première fois!
C’est la maison mère d’une sorte de prostitution artistique. C’est de là
que, tous les samedis, on expédie aux bourgs environnants deux ou trois
chanteuses et un pianiste, qui est en même temps un diseur de monologues
idiots. Ils arrivent, le soir, au café chantant où ils sont engagés. Les vieilles,
qui mangent leur soupe devant la porte, les méprisent; les ménagères et les
jeunes filles admirent l’élégance tapageuse de ces femmes; quant aux
hommes, même pour les plus rustiques, elles représentent vaguement tout
ce qu’ils imaginaient de la haute noce et du théâtre.
Elles laissent dans la petite gare un sillage de parfums grossiers, et plus
d’un adolescent mange distraitement sa salade, sans écouter le père qui
parle de la foire prochaine ou des vignes qui ont soif.
Elles sont aux filles du village ce qu’est une bouteille de champagne
fabriquée avec des acides au petit vin naturel du pays; elles sont le mal,
l’inconnu, l’attrait dangereux et charmant, l’extrême civilisation. Elles sont
surtout de pauvres êtres, d’humbles servantes et comme les bonnes à tout
faire de la chanson stupide et de la muse polissonne; et les bellâtres du
canton qui s’offrent la gommeuse ou la grande bringue navrée qui roucoule
des bêtises sentimentales, s’imaginent qu’ils ont aimé des divas illustres et
des étoiles de théâtre!...
*
* *
On a frappé ce matin à la porte
du parc, et j’ai brusquement
retrouvé la première émotion du
premier rendez-vous... Elle?...
C’était un mendiant que Jean a
chassé.
Ma gorge s’est desserrée, la
petite aiguille qui s’affolait à la
pointe de mon cœur s’est
immobilisée. J’ai été tout pareil à
ces jeunes gens qui attendent leur
maîtresse, vers quatre heures, à
Paris. Ils ont épousseté eux-mêmes
et rangé leur appartement derrière
leur femme de ménage. Ils ont mis
des fleurs dans les vases, vaporisé
dans la chambre quelque parfum,
préparé deux heures à l’avance
l’assiette de gâteaux, les tasses à thé
et la bouteille de porto. Ils ont surtout regardé la pendule. Le livre qu’ils
essayaient de lire, pour tuer le temps, ne les intéressait pas. Ils ont frotté un
à un les flacons de la toilette, compté les anneaux des rideaux sur leur
tringle de cuivre, en disant: elle viendra... oui... non... oui... non... oui...
non... heureux si le dernier anneau tombait sur oui.
A quatre heures, on sonne! Éperdus, ils vont ouvrir, et se trouvent nez à
nez avec une vieille dame asthmatique et poussive, qui s’excuse à peine et
qui s’est trompée d’étage.
J’ai été tout pareil à ces amants inquiets...
*
* *
Un quart d’heure après le départ de ce mendiant on a de nouveau frappé
à la porte, trois coups impérieux, durs, comme de quelqu’un qui
s’impatienterait en trouvant le vantail verrouillé, quand il veut entrer chez
lui et que les serviteurs tardent à ouvrir.

C’était Elle!...»
. . . . . . . . . . . .
Le vieux domestique survint à ce moment dans la chambre où je lisais.
—Eh bien, monsieur, dit-il en essayant de sourire, croyez-vous que feu
mon maître était un drôle d’homme?
Il se pencha vers la table:
—Ah! vous en êtes à son arrivée à la Tremblée. Vous n’en avez plus
pour longtemps. Ce que je ne digère point, par exemple, c’est qu’il m’a
traité de vieux comique lugubre. Je suis scrupuleux et susceptible. Oh! je ne
me plains pas, quoique, vous savez, les trois mille francs de rente dont
j’hérite, je ne les ai pas volés. Ni son père, ni lui ne m’ont jamais payé mes
gages, et je suis à leur service depuis plus de quarante ans... Enfin, il
n’aurait pas dû dire cela de moi... Achevez donc cette bouteille...
Il remplit ma coupe de vieux vin doré!
—Je vous laisse, fit-il, vous allez en avoir fini avec ce cahier. Je vous
ferai ensuite une surprise. Je vous montrerai la demoiselle; elle est encore
ici, et elle est vierge et veuve, monsieur, car mon maître est mort le jour où
il l’a reçue. Le temps se gâte, je crois qu’il va faire un gros orage...
Je repris tout de suite ma lecture:
. . . . . . . . . . . .
«Elle est enfin ici!
Je n’ai pas encore coupé les ficelles qui entourent sa boîte. Elle est
comme une voyageuse un peu lasse qui se reposerait et ne voudrait pas se
montrer trop vite à ses hôtes.
J’ai fait moi-même une toilette plus soignée. Je me négligeais depuis
quelque temps.
J’ai coupé ma barbe et ma moustache, j’ai mis un costume de flanelle
blanche et j’ai l’air d’un monsieur très fatigué dans un parc de ville d’eaux.
Quand je passe devant les volets de la chambre, instinctivement je
marche sur la pointe des pieds.
*
* *
Je crois que je prendrai mes repas devant elle. Autrefois, j’aimais
beaucoup manger en compagnie des femmes.
Un dîner d’hommes fait toujours penser à ces banquets où d’anciens
militaires du même régiment, d’authentiques badernes sorties de la même
école, la même année, se régalent à prix fixe, coude à coude et vêtus
d’habits funèbres, à l’immense table d’un salon de société que ne décore
aucune fleur.
Les hommes seuls manquent généralement de tenue, et il ne faut pas
croire qu’on a meilleur appétit et qu’on est plus à l’aise en manches de
chemise et en pantoufles.
C’est comme si l’on affirmait que le café bu dans une épaisse tasse de
faïence est plus savoureux que dans la fine et immatérielle coquille d’œuf
d’une porcelaine chinoise.
Un vrai repas, bien ordonné, est la plus aimable des choses. C’est un
luxe de civilisés qu’il faut entourer de toutes les délicatesses.
On ne mange pas du foie gras truffé, ni un sorbet à la framboise, en
sabots et en tricot de laine, sur un coin de table de cuisine, devant une
chandelle qui fume, mais en habit, avec du linge fin, sur une nappe fleurie
et à la faveur de bougies voilées d’abat-jour qui tamisent une lumière égale.
Rien alors n’est plus charmant à regarder que les jeunes femmes qui sont
la guirlande et la parure de la table.
La soie ou le velours des robes décolletées ont l’odeur des ombrelles
crépitantes chauffées au grand soleil de juillet. Des parfums naturels et des
effluves d’essences rares s’y ajoutent. Les petits carrés de truffes ou les
crevettes qui garnissent un filet de sole ont un goût unique, si une belle
brune montre, en levant le bras pour enfoncer un œillet dans son chignon, le
creux touffu de son aisselle, si, au moment où vous avalez une cuillerée de
fraises des bois assaisonnées au champagne, une blonde grasse montre ses
épaules de neige et découvre vaguement un sein dont la pointe doit être
pareille, sous les dentelles de son corsage, au fruit qui parfume votre palais.
Les gens qui prétendent que les vrais gourmands doivent s’enfermer
seuls, pour savourer des plats choisis, sont de timides maladroits.
Il faut se défier d’eux et les plaindre.
Ils passent assurément d’épouvantables nuits, car l’amour doit venir
naturellement après le dessert, comme les pêches et les muscats viennent
après les glaces et la frangipane.
La gastronomie n’est pas un art à l’usage des ermites. Lorsqu’on couche
seul, il est plus raisonnable de prendre, le soir, un bouillon léger, la moindre
des choses, un peu de confiture et une tasse de tilleul.
Les femmes, quoi qu’on dise, savent apprécier un bon repas. Cela se voit
à la façon dont elles mangent. Elles ne remplissent jamais leur assiette et ne
s’empiffrent pas de grosses viandes. Elles savent ce qu’un os de côtelette ou
de poulet peut garder de chair savoureuse et de peau rissolée.
Que d’épais bâfreurs rient de leur préférence pour les carcasses et les
croupions. C’est le reproche que pourrait faire un âne qui tond un pré au
lapin de garenne qui choisit les herbes parfumées, serpolets, thyms et
menthes sauvages... Mais à quoi vais-je penser, moi qui ne prends plus que
quelques fruits et des biscuits trempés dans un doigt de vieux vin?...
*
* *
Elle ne mangera pas. J’ai souffert quand j’étais jeune du peu de goût
dont mes amies de passage faisaient preuve, au restaurant. Je me souviens à
peine de leurs visages, mais ils reviennent parfois à la seule vue ou à
l’évocation du plat qu’elles préféraient.
Ne déjeunant et ne dînant jamais chez moi, j’ai beaucoup regardé les
femmes qui m’entouraient. Je songe à une fille avec qui je dînais assez
souvent.
Sa mère était concierge dans une maison ouvrière, du côté de
Montmartre et son père rentrait saoul à peu près chaque soir, quand elle
était enfant.
La loge sans air et sans lumière sentait le débarras et le compartiment de
troisième classe où ont dormi dix voyageurs. Elle faisait les courses pendant
que sa mère balayait l’escalier, et elle rapportait quelques sous de pain
chaud, de la charcuterie et de l’eau-de-vie. Elle s’était régalée de veau
piqué, de mirotons et de salades.
Par quel miracle était-elle devenue la splendide créature que j’admirais
pendant ces repas?
Son teint était d’une neige fouettée de roses, ses dents étaient des perles
humides, naturellement claires. Elle avait une taille de duchesse, des bras de
Vénus, de longues jambes rondes et fines, une toison énorme dont la nuance
allait du maïs mûr au cognac brûlé, et on eût juré que, née d’un mylord
spleenétique et d’une blanche lady, dans un château au bord d’un lac, elle
n’avait été nourrie que de beurrées, de crême fraîche, de gâteaux et de
puissants rosbifs anglais...
*
* *
Épouser une femme qui s’intéresse à la cuisine est une garantie de
bonheur conjugal. Même si elle n’est pas des plus jolies, la santé et la bonne
humeur qui sont les conséquences de la bonne chère, la transfigureront, et
elle sera une compagne infiniment plus agréable qu’une fille belle, froide, et
rassasiée dès le potage.
Si le mari qui rentre chez lui, las de sa journée, trouve un repas négligé,
un de ces dîners dont s’est occupée toute seule une servante, il est perdu.
La vie ne lui réservera que déboires. Après un bouillon rapidement bâclé,
trop chaud ou trop froid, plein de grumeaux et sentant le graillon, l’affaire
qui le tourmente n’aura aucune chance d’aboutir selon ses désirs.
S’il a l’impression de manger le poisson sur un évier, et le rôti sec et la
salade assaisonnée avec trop de sel et trop de vinaigre, il ne peut réussir ce
qu’il entreprendra le lendemain, et la nuit qui suit un dîner sans harmonie
ne peut pas être heureuse.
Mais aucun des soucis que nous traînons avec nous ne résistera à
l’onction d’un bon potage, au morceau de bœuf dont le sang gicle sous le
couteau, au velours d’une crême simple et parfaite.
Ce n’est pas moi qui blâmerai le
célibataire qui épouse sa cuisinière. De tous les mariages de raison, celui-là
est peut-être le plus raisonnable.
De la table au lit il n’y a qu’un pas et on le franchit sans effort.
Il est à remarquer que les premiers désirs des jeunes hommes vont aux
cuisinières bien en chair.
L’amour des maigreurs distinguées ne vient que plus tard, mais la
première impression est toujours la meilleure et la plus vraie.
Quel est l’adolescent qui n’a pas imaginé le paradis comme une cuisine
voluptueuse aux buffets pleins de volailles à la gelée, et dont les culs de
casseroles, polis et clairs ainsi que des miroirs, reflétaient une accorte fille à
la croupe dodue, aux mollets rebondis, aux bras chauds et aux seins ronds,
en train d’ôter une chemise rustique?
*
* *
Si les fiancés pouvaient observer leurs futures à l’heure des repas, cela
éviterait bien des malentendus et des divorces.
En tout cas, si j’avais quelques conseils à donner aux jeunes hommes, je
leur dirais:
—Ne demeurez pas là, extasiés comme des benêts, à regarder ses dents
quand elle boit et à vous demander par quel miracle le pain qu’elle avale, le
gigot froid, les pommes de terre, la salade, la confiture et les gâteaux secs
vont se changer en roses et en lys sur ce visage que vous convoitez.
Examinez-la calmement.
—Elle a bon appétit, mais ne se hâte point. Elle prend son temps et elle
mange posément, accueillant également tous les plats sans y revenir
jamais?...
Elle est sérieuse, patiente et dévouée. Epousez-la. C’est la compagne des
bons et des mauvais jours, celle qui ne choisira pas ailleurs et qui ne
désirera jamais que ce qu’elle possède.
—Elle a un gros appétit, et elle se hâte comme si elle était pressée par
l’heure d’un train, dans un buffet de gare. Elle est joyeuse cependant et de
bonne humeur. Elle sourit franchement entre deux bouchées?...
Si vous êtes sûr de vous, vous aurez là une femme excellente, un peu
ronde et brusque; son amour sera peut-être légèrement tyrannique, mais il
sera, aussi, robuste et solide.
Souvenez-vous, par exemple, qu’elle reprend toujours d’un plat qui lui a
plu...
—Elle déchiquette sa côtelette comme un poisson pour n’en sucer que
l’os; elle cherche, de la pointe de son couteau, une boulette de moelle?
Méfiez-vous. Elle est chicanière et soupçonneuse, jalouse aussi. Elle
fouillera dans vos poches quand vous changerez de veston...
—Elle met de chaque côté de son assiette, soigneusement, à gauche la
mie de pain, à droite la croûte?
Vous ne la connaîtrez jamais complètement. Elle est ambiguë,
méthodique, froide et secrète. Le mariage, pour elle, comporte trois
cérémonies: à la mairie, à l’église et au tribunal où se prononce le divorce.
—Si elle prend la cuisse d’un poulet rôti, épousez-la.
Elle n’est pas très délicate, mais elle est simple, bien portante et sans
détours. Elle marchera toujours sur la bonne route...
Si j’avais fait métier d’écrire, j’aurais sûrement composé un curieux
ouvrage sur la cuisine...
*
* *
Demain, elle existera!...
C’est dans ce pays que j’ai vu, pour la première fois, une femme nue. Je
crois que peu d’adolescents ont été aussi favorisés que moi et c’est le
souvenir le plus prodigieux de ma quinzième année.
J’étais un enfant studieux, sage et maladif, et, pendant les vacances, mes
seules distractions étaient la pêche et la lecture des poètes romantiques.
Un après-midi que je lisais les Orientales, sous un arbre, une petite
charrette anglaise passa sur la route et un jeune homme vêtu de blanc me fit
un salut amical.
J’allai à lui, à travers le parc.
C’était mon ami de classe Alexandre Boreuil, le fils d’un antiquaire de la
place du Forum que l’on disait fort riche.
Je lui offris de se rafraîchir, mais il refusa, craignant d’être en retard. Il
avait une course à faire à quelques kilomètres, et il me désigna une place à
côté de lui, sous le tendelet de toile écrue qui faisait une ombre claire à sa
voiture.
Il allait, me confiait-il, porter un antique objet d’art au propriétaire d’un
château des environs dont j’avais vaguement entendu parler.
Je savais que ce voisin, fort bizarre et solitaire, vivait au milieu
d’admirables collections, et j’acceptai la place que m’offrait Alexandre.
—Vous devez vous ennuyer? commença-t-il.
—Mais non, répondis-je, et je tirai les Orientales de ma poche.
Il ouvrit le bouquin, déclama une strophe, éclata de rire, et il écrasa, en
refermant le volume, une abeille qui semblait butiner les vers.
Il arrêta son cheval devant une petite porte en bois épais, toute cloutée de
bronze.
Le bouton de la sonnerie disparaissait sous le feuillage, et il nous fallut
le chercher entre les luisantes feuilles bleues d’un feston de lierre.
La porte s’ouvrit et Alexandre, ayant attaché son cheval et entravé les
roues, prit, en portant le précieux objet dans une boîte, un sentier plein de
mousse, sous des arbres de Judée.
Je le suivais, et nous aperçûmes brusquement le château.
Une vieille servante guida mon ami,—car je demeurai sur la terrasse,—à
travers un immense vestibule, vers un salon que j’apercevais devant moi et
qui devait servir de bibliothèque.
Quatre portes-fenêtres étaient ouvertes sur le jardin, et les vieux arbres et
les stores de toile jaune tamisaient à souhait l’ardente lumière.
Un homme était assis au milieu de la vaste pièce. Il paraissait quarante-
cinq ans. Une crinière grise et drue, rejetée en arrière; une barbe épaisse,
aux boucles distinctes comme celles des bronzes antiques, en faisait un être
d’un grand caractère.
On imaginait au fond du parc une victoria vernie, avec un cocher
solennel.
Je vis entrer Alexandre. Il tendait sa boîte à l’homme qui coupa les
ficelles et tira, du coton qui l’enveloppait, un petit Bacchus d’ivoire. Du plat
de sa main velue, il caressait la statuette, comme un voluptueux caresse
l’épaule bien potelée d’une maîtresse.
Lorsqu’il se leva, Alexandre prit congé, mais il s’égara sans doute dans
les couloirs, car il fut un assez long moment sans paraître.
C’est alors que j’eus la révélation de la femme.
Une grande fille entra, blonde, élancée, robuste et nue. Elle n’avait aux
pieds que des sandales retenues aux chevilles par des bandelettes dorées, et
un peigne d’écaille à son chignon.
Dans les clartés adoucies et tranquilles de l’immense salon plein de
livres, de marbres et de miroirs, elle allait sans gêne, habituée certainement
à vivre ainsi. Elle s’assit, croisa ses longues jambes blanches et prit le petit
Bacchus pour l’examiner. Puis, elle arrangea sa coiffure dans une glace et,
me tournant le dos, quitta le salon, les bras arrondis sur sa tête et pareille à
une grande amphore d’albâtre. Je ne racontai pas cela à mon ami, et j’appris
ensuite que cet homme était un singulier original, vivant seul avec cette
femme nue, dans ce château où personne ne venait jamais.
*
* *
Elle existe!... Ah! la chose n’a pas été commode... J’ai ouvert la boîte
dans l’ombre, j’ai développé la toile sur mon lit, mais avant de lui donner la
vie avec ce qui me reste de souffle, je l’ai habillée d’un peignoir de soie
chinoise, j’ai mis des bas à ses jambes plates et je l’ai chaussée, comme j’ai
pu, de mules blanches... Je l’ai vue naître
par degrés... L’étoffe s’est soulevée lentement, un pied s’est brusquement
étiré, et son visage clair s’est tourné vers moi avec ses yeux immobiles,
étonnés et extasiés. Je l’ai coiffée d’un bonnet de dentelles et je suis resté
près d’elle, en lui tenant la main, et je lui ai dit:
«—Tu n’es rien sans doute qu’une illusion, mais que sont les plus
grandes amours?
Welcome to Our Bookstore - The Ultimate Destination for Book Lovers
Are you passionate about books and eager to explore new worlds of
knowledge? At our website, we offer a vast collection of books that
cater to every interest and age group. From classic literature to
specialized publications, self-help books, and children’s stories, we
have it all! Each book is a gateway to new adventures, helping you
expand your knowledge and nourish your soul
Experience Convenient and Enjoyable Book Shopping Our website is more
than just an online bookstore—it’s a bridge connecting readers to the
timeless values of culture and wisdom. With a sleek and user-friendly
interface and a smart search system, you can find your favorite books
quickly and easily. Enjoy special promotions, fast home delivery, and
a seamless shopping experience that saves you time and enhances your
love for reading.
Let us accompany you on the journey of exploring knowledge and
personal growth!

ebookgate.com

You might also like