markhneedham/blog huggingface dataset:
PostgreSQL: ERROR:  column does not exist
Date: 2014-09-29 22:40:31

I've been playing around with PostgreSQL recently and in particular https://code.google.com/p/northwindextended/downloads/detail?name=northwind.postgre.sql&can=2&q=[the Northwind dataset] typically used as an introductory data set for relational databases.

Having imported the data I wanted to take a quick look at the employees table:

[source,sql]
----

postgres=# select * from employees limit 1;
 EmployeeID | LastName | FirstName |        Title         | TitleOfCourtesy | BirthDate  |  HireDate  |           Address           |  City   | Region | PostalCode | Country |   HomePhone    | Extension | Photo |                                                                                      Notes                                                                                      | ReportsTo |              PhotoPath
------------+----------+-----------+----------------------+-----------------+------------+------------+-----------------------------+---------+--------+------------+---------+----------------+-----------+-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+--------------------------------------
          1 | Davolio  | Nancy     | Sales Representative | Ms.             | 1948-12-08 | 1992-05-01 | 507 - 20th Ave. E.\nApt. 2A | Seattle | WA     | 98122      | USA     | (206) 555-9857 | 5467      | \x    | Education includes a BA in psychology from Colorado State University in 1970.  She also completed "The Art of the Cold Call."  Nancy is a member of Toastmasters International. |         2 | http://accweb/emmployees/davolio.bmp
(1 row)
----

That works fine but what if I only want to return the 'EmployeeID' field?

[source,sql]
----

postgres=# select EmployeeID from employees limit 1;
ERROR:  column "employeeid" does not exist
LINE 1: select EmployeeID from employees limit 1;
----

I hadn't realised (or had forgotten) that http://stackoverflow.com/questions/20878932/are-postgresql-column-names-case-sensitive[field names get lower cased] so we need to quote the name if it's been stored in mixed case:

[source,sql]
----

postgres=# select "EmployeeID" from employees limit 1;
 EmployeeID
------------
          1
(1 row)
----

From my reading the suggestion seems to be to have your field names lower cased to avoid this problem but since it's just a dummy data set I guess I'll just put up with the quoting overhead for now.
R: Deriving a new data frame column based on containing string
Date: 2014-09-29 21:37:21

I've been playing around with R data frames a bit more and one thing I wanted to do was derive a new column based on the text contained in the existing column.

I started with something like this:

[source,r]
----

> x = data.frame(name = c("Java Hackathon", "Intro to Graphs", "Hands on Cypher"))
> x
             name
1  Java Hackathon
2 Intro to Graphs
3 Hands on Cypher
----

And I wanted to derive a new column based on whether or not the session was a practical one. The https://stat.ethz.ch/R-manual/R-devel/library/base/html/grep.html[grepl] function seemed to be the best tool for the job:

[source,r]
----

> grepl("Hackathon|Hands on|Hands On", x$name)
[1]  TRUE FALSE  TRUE
----

We can then add a column to our data frame with that output:

[source,r]
----

x$practical = grepl("Hackathon|Hands on|Hands On", x$name)
----

And we end up with the following:

[source,r]
----

> x
             name practical
1  Java Hackathon      TRUE
2 Intro to Graphs     FALSE
3 Hands on Cypher      TRUE
----

Not too tricky but it took me a bit too long to figure it out so I thought I'd save future Mark some time!
R: ggplot - Plotting multiple variables on a line chart
Date: 2014-09-16 16:59:21

In my continued playing around with meetup data I wanted to plot the number of members who join the Neo4j group over time.

I started off with the variable 'byWeek' which shows how many members joined the group each week:

[source,r]
----

> head(byWeek)
Source: local data frame [6 x 2]

        week n
1 2011-06-02 8
2 2011-06-09 4
3 2011-06-30 2
4 2011-07-14 1
5 2011-07-21 1
6 2011-08-18 1
----

I wanted to plot the actual count alongside http://www.markhneedham.com/blog/2014/09/13/r-calculating-rolling-or-moving-averages/[a rolling average] for which I created the following data frame:

[source,r]
----

library(zoo)
joinsByWeek = data.frame(actual = byWeek$n,
                         week = byWeek$week,
                         rolling = rollmean(byWeek$n, 4, fill = NA, align=c("right")))
----

[source,text]
----

> head(joinsByWeek, 10)
   actual       week rolling
1       8 2011-06-02      NA
2       4 2011-06-09      NA
3       2 2011-06-30      NA
4       1 2011-07-14    3.75
5       1 2011-07-21    2.00
6       1 2011-08-18    1.25
7       1 2011-10-13    1.00
8       2 2011-11-24    1.25
9       1 2012-01-05    1.25
10      3 2012-01-12    1.75
----

The next step was to work out how to http://stackoverflow.com/questions/3777174/plotting-two-variables-as-lines-using-ggplot2-on-the-same-graph[plot both 'rolling' and 'actual' on the same line chart]. The easiest way is to make two calls to 'geom_line', like so:

[source,r]
----

ggplot(joinsByWeek, aes(x = week)) +
  geom_line(aes(y = rolling), colour="blue") +
  geom_line(aes(y = actual), colour = "grey") +
  ylab(label="Number of new members") +
  xlab("Week")
----

image::{{<siteurl>}}/uploads/2014/09/2014-09-16_21-57-14.png[2014 09 16 21 57 14,600]

Alternatively we can make use of the 'melt' function from the +++<cite>+++http://www.statmethods.net/management/reshape.html[reshape]+++</cite>+++ library\...

[source,r]
----

library(reshape)
meltedJoinsByWeek = melt(joinsByWeek, id = 'week')
----

[source,text]
----

> head(meltedJoinsByWeek, 20)
   week variable value
1     1   actual     8
2     2   actual     4
3     3   actual     2
4     4   actual     1
5     5   actual     1
6     6   actual     1
7     7   actual     1
8     8   actual     2
9     9   actual     1
10   10   actual     3
11   11   actual     1
12   12   actual     2
13   13   actual     4
14   14   actual     2
15   15   actual     3
16   16   actual     5
17   17   actual     1
18   18   actual     2
19   19   actual     1
20   20   actual     2
----

\...which then means we can plot the chart with a single call to geom_line:

[source,r]
----

ggplot(meltedJoinsByWeek, aes(x = week, y = value, colour = variable)) +
  geom_line() +
  ylab(label="Number of new members") +
  xlab("Week Number") +
  scale_colour_manual(values=c("grey", "blue"))
----

image::{{<siteurl>}}/uploads/2014/09/2014-09-16_22-17-40.png[2014 09 16 22 17 40,600]
Neo4j: LOAD CSV - Column is null
Date: 2014-09-24 20:21:59

One problem I've seen a few people have recently when using Neo4j's http://docs.neo4j.org/chunked/stable/query-load-csv.html[LOAD CSV] function is dealing with CSV files that have dodgy hidden characters at the beginning of the header line.

For example, consider an import of this CSV file:

[source,text]
----

$ cat ~/Downloads/dodgy.csv
userId,movieId
1,2
----

We might start by checking which columns it has:

[source,cypher]
----

$ load csv with headers from "file:/Users/markneedham/Downloads/dodgy.csv" as line return line;
+----------------------------------+
| line                             |
+----------------------------------+
| {userId -> "1", movieId -> "2"} |
+----------------------------------+
1 row
----

Looks good so far but what about if we try to return just 'userId'?

[source,cypher]
----

$ load csv with headers from "file:/Users/markneedham/Downloads/dodgy.csv" as line return line.userId;
+-------------+
| line.userId |
+-------------+
| <null>      |
+-------------+
1 row
----

Hmmm it's null\...what about 'movieId'?

[source,cypher]
----

$ load csv with headers from "file:/Users/markneedham/Downloads/dodgy.csv" as line return line.movieId;
+--------------+
| line.movieId |
+--------------+
| "2"          |
+--------------+
1 row
----

That works fine so immediately we can suspect there are hidden characters at the beginning of the first line of the file.

The easiest way to check if this is the case is open the file using a Hex Editor - I quite like http://ridiculousfish.com/hexfiend/[Hex Fiend] for the Mac.

If we look at dodgy.csv we'll see the following:

image::{{<siteurl>}}/uploads/2014/09/2014-09-24_21-20-06.png[2014 09 24 21 20 06,465]

Let's delete the highlighted characters and try our cypher query again:

[source,cypher]
----

$ load csv with headers from "file:/Users/markneedham/Downloads/dodgy.csv" as line return line.userId;
+-------------+
| line.userId |
+-------------+
| "1"         |
+-------------+
1 row
----

All is well again, but something to keep in mind if you see a LOAD CSV near you behaving badly.
R: ggplot - Plotting a single variable line chart (geom_line requires the following missing aesthetics: y)
Date: 2014-09-13 11:41:39

I've been learning http://www.markhneedham.com/blog/2014/09/13/r-calculating-rolling-or-moving-averages/[how to do moving averages in R] and having done that calculation I wanted to plot these variables on a line chart using ggplot.

The vector of rolling averages looked like this:

[source,r]
----

> rollmean(byWeek$n, 4)
  [1]  3.75  2.00  1.25  1.00  1.25  1.25  1.75  1.75  1.75  2.50  2.25  2.75  3.50  2.75  2.75
 [16]  2.25  1.50  1.50  2.00  2.00  2.00  2.00  1.25  1.50  2.25  2.50  3.00  3.25  2.75  4.00
 [31]  4.25  5.25  7.50  6.50  5.75  5.00  3.50  4.00  5.75  6.25  6.25  6.00  5.25  6.25  7.25
 [46]  7.75  7.00  4.75  2.75  1.75  2.00  4.00  5.25  5.50 11.50 11.50 12.75 14.50 12.50 11.75
 [61] 11.00  9.25  5.25  4.50  3.25  4.75  7.50  8.50  9.25 10.50  9.75 15.25 16.00 15.25 15.00
 [76] 10.00  8.50  6.50  4.25  3.00  4.25  4.75  7.50 11.25 11.00 11.50 10.00  6.75 11.25 12.50
 [91] 12.00 11.50  6.50  8.75  8.50  8.25  9.50  8.50  8.75  9.50  8.00  4.25  4.50  7.50  9.00
[106] 12.00 19.00 19.00 22.25 23.50 22.25 21.75 19.50 20.75 22.75 22.75 24.25 28.00 23.00 26.00
[121] 24.25 21.50 26.00 24.00 28.25 25.50 24.25 31.50 31.50 35.75 35.75 29.00 28.50 27.25 25.50
[136] 27.50 26.00 23.75
----

I initially tried to plot a line chart like this:

[source,r]
----

library(ggplot2)
library(zoo)
rollingMean = rollmean(byWeek$n, 4)
qplot(rollingMean) + geom_line()
----

which resulted in this error:

[source,r]
----

stat_bin: binwidth defaulted to range/30. Use 'binwidth = x' to adjust this.
Error: geom_line requires the following missing aesthetics: y
----

It turns out we need to provide an x and y value if we want to draw a line chart. In this case http://stackoverflow.com/questions/13837565/how-to-plot-one-variable-in-ggplot[we'll generate the 'x' value] - we only care that the y values get plotted in order from left to right:

[source,r]
----

qplot(1:length(rollingMean), rollingMean, xlab ="Week Number") + geom_line()
----

image::{{<siteurl>}}/uploads/2014/09/2014-09-13_16-58-57.png[2014 09 13 16 58 57,600]

If we want to use the 'ggplot' function then we need to put everything into a data frame first and then plot it:

[source,r]
----

ggplot(data.frame(week = 1:length(rollingMean), rolling = rollingMean),
       aes(x = week, y = rolling)) +
  geom_line()
----

image::{{<siteurl>}}/uploads/2014/09/2014-09-13_17-11-13.png[2014 09 13 17 11 13,600]
R: dplyr - group_by dynamic or programmatic field / variable (Error: index out of bounds)
Date: 2014-08-29 09:13:00

In my http://www.markhneedham.com/blog/2014/08/29/r-grouping-by-week-month-quarter/[last blog post] I showed how to group timestamp based data by week, month and quarter and by the end we had the following code samples using http://cran.r-project.org/web/packages/dplyr/index.html[dplyr] and http://cran.r-project.org/web/packages/zoo/index.html[zoo]:

[source,r]
----

library(RNeo4j)
library(zoo)

timestampToDate <- function(x) as.POSIXct(x / 1000, origin="1970-01-01", tz = "GMT")

query = "MATCH (:Person)-[:HAS_MEETUP_PROFILE]->()-[:HAS_MEMBERSHIP]->(membership)-[:OF_GROUP]->(g:Group {name: \"Neo4j - London User Group\"})
         RETURN membership.joined AS joinTimestamp"
meetupMembers = cypher(graph, query)

meetupMembers$joinDate <- timestampToDate(meetupMembers$joinTimestamp)
meetupMembers$monthYear <- as.Date(as.yearmon(meetupMembers$joinDate))
meetupMembers$quarterYear <- as.Date(as.yearqtr(meetupMembers$joinDate))

meetupMembers %.% group_by(week) %.% summarise(n = n())
meetupMembers %.% group_by(monthYear) %.% summarise(n = n())
meetupMembers %.% group_by(quarterYear) %.% summarise(n = n())
----

As you can see there's quite a bit of duplication going on - the only thing that changes in the last 3 lines is the name of the field that we want to group by.

I wanted to pull this code out into a function and my first attempt was this:

[source,r]
----

groupMembersBy = function(field) {
  meetupMembers %.% group_by(field) %.% summarise(n = n())
}
----

And now if we try to group by week:

[source,r]
----

> groupMembersBy("week")
 Show Traceback

 Rerun with Debug
 Error: index out of bounds
----

It turns out if we want to do this then http://stackoverflow.com/questions/21815060/dplyr-how-to-use-group-by-inside-a-function[we actually want the +++<cite>+++regroup+++</cite>+++ function rather than +++<cite>+++group_by+++</cite>+++]:

[source,r]
----

groupMembersBy = function(field) {
  meetupMembers %.% regroup(list(field)) %.% summarise(n = n())
}
----

And now if we group by week:

[source,r]
----

> head(groupMembersBy("week"), 20)
Source: local data frame [20 x 2]

         week n
1  2011-06-02 8
2  2011-06-09 4
3  2011-06-16 1
4  2011-06-30 2
5  2011-07-14 1
6  2011-07-21 1
7  2011-08-18 1
8  2011-10-13 1
9  2011-11-24 2
10 2012-01-05 1
11 2012-01-12 3
12 2012-02-09 1
13 2012-02-16 2
14 2012-02-23 4
15 2012-03-01 2
16 2012-03-08 3
17 2012-03-15 5
18 2012-03-29 1
19 2012-04-05 2
20 2012-04-19 1
----

Much better!
Ruby: Receive JSON in request body
Date: 2014-08-17 12:21:15

I've been building a little http://www.sinatrarb.com/intro.html[Sinatra] app to play around with the Google Drive API and one thing I struggled with was processing JSON posted in the request body.

I came across a few posts which suggested that the request body would be available as +++<cite>+++params['data']+++</cite>+++ or +++<cite>+++request['data']+++</cite>+++ but after trying several ways of sending a POST request that doesn't seem to be the case.

I eventually came across http://stackoverflow.com/questions/17049569/how-to-parse-json-request-body-in-sinatra-just-once-and-expose-it-to-all-routes[this StackOverflow post] which shows how to do it:

[source,ruby]
----

require 'sinatra'
require 'json'

post '/somewhere/' do
  request.body.rewind
  request_payload = JSON.parse request.body.read

  p request_payload

  "win"
end
----

I can then POST to that endpoint and see the JSON printed back on the console:

+++<cite>+++dummy.json+++</cite>+++

[source,json]
----

{"i": "am json"}
----

[source,bash]
----

$ curl -H "Content-Type: application/json" -XPOST http://localhost:9393/somewhere/ -d @dummy.json
----

[source,text]
----

{"i"=>"am json"}
----

Of course if I'd just http://www.sinatrarb.com/intro.html[RTFM] I could have found this out much more quickly!
Ruby: Google Drive - Error=BadAuthentication (GoogleDrive::AuthenticationError) Info=InvalidSecondFactor
Date: 2014-08-17 01:49:10

I've been using the https://github.com/gimite/google-drive-ruby[Google Drive gem] to try and interact with my Google Drive account and almost immediately ran into problems trying to login.

I started out with the following code:

[source,ruby]
----

require "rubygems"
require "google_drive"

session = GoogleDrive.login("me@mydomain.com", "mypassword")
----

I'll move it to use OAuth when I put it into my application but for spiking this approach works. Unfortunately I got the following error when running the script:

[source,text]
----

/Users/markneedham/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/google_drive-0.3.10/lib/google_drive/session.rb:93:in `rescue in login': Authentication failed for me@mydomain.com: Response code 403 for post https://www.google.com/accounts/ClientLogin: Error=BadAuthentication (GoogleDrive::AuthenticationError)
Info=InvalidSecondFactor
	from /Users/markneedham/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/google_drive-0.3.10/lib/google_drive/session.rb:86:in `login'
	from /Users/markneedham/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/google_drive-0.3.10/lib/google_drive/session.rb:38:in `login'
	from /Users/markneedham/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/google_drive-0.3.10/lib/google_drive.rb:18:in `login'
	from src/gdoc.rb:15:in `<main>'
----

Since I have two factor authentication enabled on my account https://github.com/gimite/google-drive-ruby/issues/40[it turns out] that I need to create an https://security.google.com/settings/security/apppasswords[app password] to login:

image::{{<siteurl>}}/uploads/2014/08/2014-08-17_02-47-03.png[2014 08 17 02 47 03,600]

It will then pop up with a password that we can use to login (I have revoked this one!):

image::{{<siteurl>}}/uploads/2014/08/2014-08-17_02-46-29.png[2014 08 17 02 46 29,600]

We can then use this password instead and everything works fine:

[source,ruby]
----


require "rubygems"
require "google_drive"

session = GoogleDrive.login("me@mydomain.com", "tuceuttkvxbvrblf")
----
Where does r studio install packages/libraries?
Date: 2014-08-14 10:24:52

As a newbie to R I wanted to look at the source code of some of the libraries/packages that I'd installed via R Studio which I initially struggled to do as I wasn't sure where the packages had been installed.

I eventually came across http://stackoverflow.com/questions/15170399/changing-r-default-library-path-using-libpaths-in-rprofile-site-fails-to-work[a StackOverflow post] which described the +++<cite>+++http://stat.ethz.ch/R-manual/R-devel/library/base/html/libPaths.html[.libPaths]+++</cite>+++ function which tells us where that is:

[source,r]
----

> .libPaths()
[1] "/Library/Frameworks/R.framework/Versions/3.1/Resources/library"
----

If we want to see which libraries are installed we can use the +++<cite>+++http://stat.ethz.ch/R-manual/R-devel/library/base/html/list.files.html[list.files]+++</cite>+++ function:

[source,r]
----

> list.files("/Library/Frameworks/R.framework/Versions/3.1/Resources/library")
 [1] "alr3"         "assertthat"   "base"         "bitops"       "boot"         "brew"
 [7] "car"          "class"        "cluster"      "codetools"    "colorspace"   "compiler"
[13] "data.table"   "datasets"     "devtools"     "dichromat"    "digest"       "dplyr"
[19] "evaluate"     "foreign"      "formatR"      "Formula"      "gclus"        "ggplot2"
[25] "graphics"     "grDevices"    "grid"         "gridExtra"    "gtable"       "hflights"
[31] "highr"        "Hmisc"        "httr"         "KernSmooth"   "knitr"        "labeling"
[37] "Lahman"       "lattice"      "latticeExtra" "magrittr"     "manipulate"   "markdown"
[43] "MASS"         "Matrix"       "memoise"      "methods"      "mgcv"         "mime"
[49] "munsell"      "nlme"         "nnet"         "openintro"    "parallel"     "plotrix"
[55] "plyr"         "proto"        "RColorBrewer" "Rcpp"         "RCurl"        "reshape2"
[61] "RJSONIO"      "RNeo4j"       "Rook"         "rpart"        "rstudio"      "scales"
[67] "seriation"    "spatial"      "splines"      "stats"        "stats4"       "stringr"
[73] "survival"     "swirl"        "tcltk"        "testthat"     "tools"        "translations"
[79] "TSP"          "utils"        "whisker"      "xts"          "yaml"         "zoo"
----

We can then drill into those directories to find the appropriate file - in this case I wanted to look at one of the http://cran.r-project.org/web/packages/Rook/Rook.pdf[Rook] examples:

[source,text]
----

$ cat /Library/Frameworks/R.framework/Versions/3.1/Resources/library/Rook/exampleApps/helloworld.R
app <- function(env){
    req <- Rook::Request$new(env)
    res <- Rook::Response$new()
    friend <- 'World'
    if (!is.null(req$GET()[['friend']]))
	friend <- req$GET()[['friend']]
    res$write(paste('<h1>Hello',friend,'</h1>\n'))
    res$write('What is your name?\n')
    res$write('<form method="GET">\n')
    res$write('<input type="text" name="friend">\n')
    res$write('<input type="submit" name="Submit">\n</form>\n<br>')
    res$finish()
}
----
R: Rook - Hello world example - 'Cannot find a suitable app in file'
Date: 2014-08-22 11:05:54

I've been playing around with the http://cran.r-project.org/web/packages/Rook/README.html[Rook] library and struggled a bit getting a basic Hello World application up and running so I thought I should document it for future me.

I wanted to spin up a web server using Rook and serve a page with the text 'Hello World'. I https://docs.google.com/presentation/d/1AT8nQo6jrZ9SwPgdwqkHZI_2SeqGQormy-EWWmm_rlk/edit#slide=id.i112[started with the following code]:

[source,r]
----

library(Rook)
s <- Rhttpd$new()

s$add(name='MyApp',app='helloworld.R')
s$start()
s$browse("MyApp")
----

where +++<cite>+++helloWorld.R+++</cite>+++ contained the following code:

[source,r]
----

function(env){
  list(
    status=200,
    headers = list(
      'Content-Type' = 'text/html'
    ),
    body = paste('<h1>Hello World!</h1>')
  )
}
----

Unfortunately that failed on the 's$add' line with the following error message:

[source,r]
----

> s$add(name='MyApp',app='helloworld.R')
Error in .Object$initialize(...) :
  Cannot find a suitable app in file helloworld.R
----

I hadn't realised that you actually need to assign that function to a variable 'app' in order for it to be picked up:

[source,r]
----

app <- function(env){
  list(
    status=200,
    headers = list(
      'Content-Type' = 'text/html'
    ),
    body = paste('<h1>Hello World!</h1>')
  )
}
----

Once I fixed that everything seemed to work as expected:s

[source,r]
----

> s
Server started on 127.0.0.1:27120
[1] MyApp http://127.0.0.1:27120/custom/MyApp

Call browse() with an index number or name to run an application.
----
Neo4j: LOAD CSV - Handling empty columns
Date: 2014-08-22 12:51:36

A common problem that people encounter when trying to import CSV files into Neo4j using Cypher's http://docs.neo4j.org/chunked/stable/cypherdoc-importing-csv-files-with-cypher.html[LOAD CSV] command is how to handle empty or 'null' entries in said files.

For example let's try and import the following file which has 3 columns, 1 populated, 2 empty:

[source,text]
----

$ cat /tmp/foo.csv
a,b,c
mark,,
----

[source,cypher]
----

load csv with headers from "file:/tmp/foo.csv" as row
MERGE (p:Person {a: row.a})
SET p.b = row.b, p.c = row.c
RETURN p
----

When we execute that query we'll see that our Person node has properties 'b' and 'c' with no value:

[source,cypher]
----

==> +-----------------------------+
==> | p                           |
==> +-----------------------------+
==> | Node[5]{a:"mark",b:"",c:""} |
==> +-----------------------------+
==> 1 row
==> Nodes created: 1
==> Properties set: 3
==> Labels added: 1
==> 26 ms
----

That isn't what we want - we don't want those properties to be set unless they have a value.

TO achieve this we need to introduce a conditional when setting the 'b' and 'c' properties. We'll assume that 'a' is always present as that's the key for our Person nodes.

The following query will do what we want:

[source,cypher]
----

load csv with headers from "file:/tmp/foo.csv" as row
MERGE (p:Person {a: row.a})
FOREACH(ignoreMe IN CASE WHEN trim(row.b) <> "" THEN [1] ELSE [] END | SET p.b = row.b)
FOREACH(ignoreMe IN CASE WHEN trim(row.c) <> "" THEN [1] ELSE [] END | SET p.c = row.c)
RETURN p
----

Since there's no if or else statements in cypher we create our own conditional statement by using FOREACH. If there's a value in the CSV column then we'll loop once and set the property and if not we won't loop at all and therefore no property will be set.

[source,cypher]
----

==> +-------------------+
==> | p                 |
==> +-------------------+
==> | Node[4]{a:"mark"} |
==> +-------------------+
==> 1 row
==> Nodes created: 1
==> Properties set: 1
==> Labels added: 1
----