debkeepr integrates non-decimal currencies that use the tripartite system of pounds, shillings, and pence into the methodologies of Digital Humanities and the practices of reproducible research. The package makes it possible for historical non-decimal currencies to behave like decimalized numeric values through the implementation of the
deb_decimal vector classes or types. These types are based on the infrastructure provided by the vctrs package.
debkkeepr simplifies the process of performing arithmetic calculations with non-decimal currencies — such as adding £3 13s. 4d. sterling to £8 15s. 9d. sterling — and also provides a basis for analyzing account books with thousands of transactions recorded in non-decimal currencies. The name of the
debkeepr package derives from this latter capability of analyzing historical account books that often used double-entry bookkeeping.
You can install
debkeepr from GitHub with remotes:
# install.packages("remotes") remotes::install_github("jessesadler/debkeepr")
Please open an issue if you have any questions, comments, or requests.
debkeepr package uses the nomenclature of l, s, and d to represent pounds, shillings, and pence units in non-decimal currencies. The abbreviations derive from the Latin terms libra, solidus, and denarius. The libra was a Roman measurement of weight, while the solidus and denarius were both Roman coins. The denarius was a silver coin from the era of the Republic, in contrast to the golden solidus that was issued in the Late Empire. As the production of silver coins overtook that of gold by the 8th century, a solidus came to represent 12 silver denarii coins, and 240 denarii were — for a time — made from one libra or pound of silver. The custom of counting coins in dozens (solidi) and scores of dozens (librae) spread throughout the Carolingian Empire and became engrained in much of Europe. However, a variety of currencies or monies of account used other bases for the solidus and denarius units.
debkeepr provides a consistent manner for dealing with any set of bases within a tripartite system through the
bases attribute of
deb_decimal vectors and the
unit attribute of
Translations of libra, solidus, and denarius units:
deb_decimaltypes and their use as vectors and as columns in data frames.
dafforne_accountsdata provided in
deb_decimal types are implemented to deal with two interrelated problems inherent in historical currencies.
deb_lsd type maintains the tripartite structure of non-decimal currencies and provides a
bases attribute to record the bases for the shillings and pence units. The
deb_decimal type also contains a
bases attribute, as well as a
unit attribute to track which unit the decimalized value represents (pounds, shillings, or pence). The print methods for both types show the
bases attribute, and
deb_decimal vectors include the
Let’s see how this works in practice, beginning with
deb_lsd vectors. Note that all of the functions in
debkeepr begin with the prefix
deb_, which is short for double-entry bookkeeping.
A primary reason for the creation of the
deb_lsd type is to simplify arithmetic calculations with non-decimal currency. Doing calculations by hand requires the use of compound unit arithmetic and normalization.
All implemented arithmetic calculations with
deb_lsd vectors —
-, etc. — automatically normalize the values according to the
bases attribute. In addition, you can manually normalize non-standard values with
# Perform arithmetic lsd1 + lsd2 #> <deb_lsd> #>  12:9s:1d #> # Bases: 20s 12d lsd2 - lsd1 #> <deb_lsd> #>  5:2s:5d #> # Bases: 20s 12d lsd2 * 2 - lsd1 #> <deb_lsd> #>  13:18s:2d #> # Bases: 20s 12d # Normalize a non-standard value to default bases deb_normalize(deb_lsd(132, 53, 35)) #> <deb_lsd> #>  134:15s:11d #> # Bases: 20s 12d
Both types allow the user to define the solidus and denarius units of values, enabling integration of currencies that do not use the standardized bases. For example, the Polish florin found in Dafforne’s practice journal used the non-standard bases of 30 gros of 18 denars.
# Create deb_lsd vector with standard bases of 20s. 12d. (lsd3 <- deb_lsd(l = c(28, 32, 54, 18), s = c(15, 8, 18, 12), d = c(8, 11, 7, 9))) #> <deb_lsd> #>  28:15s:8d 32:8s:11d 54:18s:7d 18:12s:9d #> # Bases: 20s 12d # Same numerical values as Polish florins (florins <- deb_lsd(l = c(28, 32, 54, 18), s = c(15, 8, 18, 12), d = c(8, 11, 7, 9), bases = c(30, 18))) #> <deb_lsd> #>  28:15s:8d 32:8s:11d 54:18s:7d 18:12s:9d #> # Bases: 30s 18d # Different outcome with sum due to the different bases sum(lsd3) #> <deb_lsd> #>  134:15s:11d #> # Bases: 20s 12d sum(florins) #> <deb_lsd> #>  133:24s:17d #> # Bases: 30s 18d # Vectors with different bases cannot be combined since # their relationship is unknown. Doing so results in an error. sum(lsd3, florins) #> Error: `bases` attributes must be equal to combine <deb_lsd> or <deb_decimal> vectors.
In contrast to the tripartite structure of
deb_decimal vectors represent non-decimal currencies in the more familiar decimal form. Internally,
deb_decimal vectors are built on
double() vectors. These decimalized vectors are linked to their non-decimal form through the
# Create deb_decimal from numeric vector (dec1 <- deb_decimal(c(5.525, 12.235, 8.45))) #> <deb_decimal> #>  5.525 12.235 8.450 #> # Unit: libra #> # Bases: 20s 12d # Same currency values in solidus unit (dec2 <- deb_decimal(c(110.5, 244.7, 169), unit = "s")) #> <deb_decimal> #>  110.5 244.7 169.0 #> # Unit: solidus #> # Bases: 20s 12d # Equality between different units dec1 == dec2 #>  TRUE TRUE TRUE # Equality between deb_lsd and deb_decimal vectors # £5 10s. 6d. is equal to 1,326 pence deb_lsd(5, 10, 6) == deb_decimal(1326, unit = "d") #>  TRUE
When working with decimalized data is preferable, the
deb_decimal type makes casting from and to
deb_lsd possible without losing any metadata about the
bases and therefore the actual value being represented.
deb_decimal vectors can also be combined with numeric vectors or cast from and to numeric vectors.
debkeepr uses an internal conversion hierarchy of
# Combining deb_lsd and deb_decimal gives a deb_lsd vector c(dec1, lsd1, lsd2) #> <deb_lsd> #>  5:10s:6d 12:4s:8.4d 8:9s:0d 3:13s:4d 8:15s:9d #> # Bases: 20s 12d c(dec1, lsd1, 8.25) #> <deb_lsd> #>  5:10s:6d 12:4s:8.4d 8:9s:0d 3:13s:4d 8:5s:0d #> # Bases: 20s 12d # Cast between deb_lsd and deb_decimal vectors deb_as_lsd(dec1) #> <deb_lsd> #>  5:10s:6d 12:4s:8.4d 8:9s:0d #> # Bases: 20s 12d deb_as_decimal(lsd3) #> <deb_decimal> #>  28.78333 32.44583 54.92917 18.63750 #> # Unit: libra #> # Bases: 20s 12d deb_as_decimal(florins) #> <deb_decimal> #>  28.51481 32.28704 54.61296 18.41667 #> # Unit: libra #> # Bases: 30s 18d # Represented by solidus/shillings unit deb_as_decimal(lsd3, unit = "s") #> <deb_decimal> #>  575.6667 648.9167 1098.5833 372.7500 #> # Unit: solidus #> # Bases: 20s 12d # Represented by denarius/pence unit deb_as_decimal(lsd3, unit = "d") #> <deb_decimal> #>  6908 7787 13183 4473 #> # Unit: denarius #> # Bases: 20s 12d # Either type can be cast to base numeric, which, # of course, leads to the loss of all metadata as.numeric(lsd3) #>  28.78333 32.44583 54.92917 18.63750 as.numeric(dec1) #>  5.525 12.235 8.450
See the Getting Started with debkeepr vignette for an in depth discussion of the similarities and differences between the two types.
deb_lsdtype has the advantage of maintaining the structure and values used by non-decimal currencies, making it easier to identify and present such values.
deb_decimalimplements a wider array of mathematical functions and arithmetic operations than
deb_decimalare based on the vctrs package, both types act as expected in data frames or tibbles columns. From dplyr 1.0.0 — which is the minimal version used by debkeepr — all dplyr functions work on both
deb_lsdvectors. In contrast,
deb_decimalvectors work properly with
ggplot2, though explicitly identifying the scale as continuous — with
scale_x_continuous()— is needed to avoid the appearance of a message.
deb_decimalvectors cannot be combined in a single function if their
basesdiffer. The only way to transform the bases of
deb_decimalvectors is explicitly with
deb_convert_bases(). This prevents mistakenly combining two different currencies together without properly converting their values.