Immediate download Python Data Analysis Numpy, Matplotlib and Pandas Bernd Klein ebooks 2024
Immediate download Python Data Analysis Numpy, Matplotlib and Pandas Bernd Klein ebooks 2024
com
https://ebookmeta.com/product/python-data-analysis-numpy-
matplotlib-and-pandas-bernd-klein/
OR CLICK HERE
DOWLOAD NOW
https://ebookmeta.com/product/python-data-analytics-with-pandas-numpy-
and-matplotlib-3rd-edition-fabio-nelli/
ebookmeta.com
https://ebookmeta.com/product/python-data-analytics-with-pandas-numpy-
and-matplotlib-3rd-edition-fabio-nelli-2/
ebookmeta.com
https://ebookmeta.com/product/urban-mobility-and-social-equity-in-
latin-america-evidence-concepts-methods-1st-edition-daniel-oviedo/
ebookmeta.com
Find Build Sell How I Turned a 100 Backyard Bar into a 100
Million Pub Empire 1st Edition Stephen J Hunt
https://ebookmeta.com/product/find-build-sell-how-i-
turned-a-100-backyard-bar-into-a-100-million-pub-empire-1st-edition-
stephen-j-hunt/
ebookmeta.com
https://ebookmeta.com/product/essentials-of-services-
marketing-4e-jochen-wirtz/
ebookmeta.com
https://ebookmeta.com/product/case-studies-in-advanced-engineering-
design-proceedings-of-the-1st-international-symposium-1st-edition-c-
spitas/
ebookmeta.com
https://ebookmeta.com/product/bacterial-persistence-2nd-edition-
natalie-verstraeten-jan-michiels/
ebookmeta.com
An Analysis of John Locke's Two Treatises of Government
(The Macat Library) 1st Edition Jeremy Kleidosty
https://ebookmeta.com/product/an-analysis-of-john-lockes-two-
treatises-of-government-the-macat-library-1st-edition-jeremy-
kleidosty/
ebookmeta.com
Data
Analysis
by
Bernd Klein
bodenseo
© 2021 Bernd Klein
All rights reserved. No portion of this book may be reproduced or used in any
manner without written permission from the copyright owner.
www.python-course.eu
Python Course
Data Analysis With
Python by Bernd
Klein
Numpy Tutorial ..........................................................................................................................8
Numpy Tutorial: Creating Arrays.............................................................................................17
Data Type Objects, dtype..........................................................................................................36
Numerical Operations on Numpy Arrays.................................................................................48
Numpy Arrays: Concatenating, Flattening and Adding Dimensions .......................................68
Python, Random Numbers and Probability ..............................................................................79
Weighted Probabilities..............................................................................................................90
Synthetical Test Data With Python.........................................................................................119
Numpy: Boolean Indexing......................................................................................................136
Matrix Multiplicaion, Dot and Cross Product ........................................................................143
Reading and Writing Data Files .............................................................................................149
Overview of Matplotlib ..........................................................................................................157
Format Plots............................................................................................................................168
Matplotlib Tutorial..................................................................................................................172
Shading Regions with fill_between() .....................................................................................183
Matplotlib Tutorial: Spines and Ticks ....................................................................................186
Matplotlib Tutorial, Adding Legends and Annotations..........................................................197
Matplotlib Tutorial: Subplots .................................................................................................212
Exercise ....................................................................................................................................44
Exercise ....................................................................................................................................44
Matplotlib Tutorial: Gridspec .................................................................................................239
GridSpec using SubplotSpec ..................................................................................................244
Matplotlib Tutorial: Histograms and Bar Plots ......................................................................248
Matplotlib Tutorial: Contour Plots .........................................................................................268
Introduction into Pandas.........................................................................................................303
Data Structures .......................................................................................................................305
Accessing and Changing values of DataFrames.....................................................................343
Pandas: groupby .....................................................................................................................361
Reading and Writing Data ......................................................................................................380
Dealing with NaN...................................................................................................................394
Binning in Python and Pandas................................................................................................404
Expenses and Income Example ..............................................................................................465
Net Income Method Example.................................................................................................478
3
NUMERICAL PROGRAMMING WITH
PYTHON
Numerical Computing defines an area of computer science and mathematics dealing with algorithms for
numerical approximations of problems from mathematical or numerical analysis, in other words: Algorithms
solving problems involving continuous variables. Numerical analysis is used to solve science and engineering
problems.
Data science is an interdisciplinary subject which includes for example statistics and computer science,
especially programming and problem solving skills. Data Science includes everything which is necessary to
create and prepare data, to manipulate, filter and clense data and to analyse data. Data can be both structured
and unstructured. We could also say Data Science includes all the techniques needed to extract and gain
information and insight from data.
Data Science is an umpbrella term which incorporates data analysis, statistics, machine learning and other
related scientific fields in order to understand and analyze data.
Another term occuring quite often in this context is "Big Data". Big Data is for sure one of the most often used
buzzwords in the software-related marketing world. Marketing managers have found out that using this term
can boost the sales of their products, regardless of the fact if they are really dealing with big data or not. The
term is often used in fuzzy ways.
Big data is data which is too large and complex, so that it is hard for data-processing application software to
deal with them. The problems include capturing and collecting data, data storage, search the data, visualization
of the data, querying, and so on.
• volume:
the sheer amount of data, whether it will be giga-, tera-, peta- or exabytes
• velocity:
the speed of arrival and processing of data
• veracity:
4
uncertainty or imprecision of data
• variety:
the many sources and types of data both structured and unstructured
The big question is how useful Python is for these purposes. If we would only use Python without any special
modules, this language could only poorly perform on the previously mentioned tasks. We will describe the
necessary tools in the following chapter.
5
Numpy is a module which provides the basic data structures,
implementing multi-dimensional arrays and matrices. Besides
that the module supplies the necessary functionalities to create
and manipulate these data structures. SciPy is based on top of
Numpy, i.e. it uses the data structures provided by NumPy. It
extends the capabilities of NumPy with further useful functions
for minimization, regression, Fourier-transformation and many
others.
The principal disadvantage of MATLAB against Python are the costs. Python with NumPy, SciPy, Matplotlib
and Pandas is completely free, whereas MATLAB can be very expensive. "Free" means both "free" as in "free
beer" and "free" as in "freedom"! Even though MATLAB has a huge number of additional toolboxes available,
Python has the advantage that it is a more modern and complete programming language. Python is continually
becoming more powerful by a rapidly growing number of specialized modules.
Python in combination with Numpy, Scipy, Matplotlib and Pandas can be used as a complete replacement for
MATLAB.
6
7
NUMPY TUTORIAL
INTRODUCTION
NumPy is a module for Python. The name is an acronym for
"Numeric Python" or "Numerical Python". It is pronounced
/ˈnʌmpaɪ/ (NUM-py) or less often /ˈnʌmpi (NUM-pee)). It is an
extension module for Python, mostly written in C. This makes
sure that the precompiled mathematical and numerical functions
and functionalities of Numpy guarantee great execution speed.
SciPy (Scientific Python) is often mentioned in the same breath with NumPy. SciPy needs Numpy, as it is
based on the data structures of Numpy and furthermore its basic creation and manipulation functions. It
extends the capabilities of NumPy with further useful functions for minimization, regression, Fourier-
transformation and many others.
Both NumPy and SciPy are not part of a basic Python installation. They have to be installed after the Python
installation. NumPy has to be installed before installing SciPy.
(Comment: The diagram of the image on the right side is the graphical visualisation of a matrix with 14 rows
and 20 columns. It's a so-called Hinton diagram. The size of a square within this diagram corresponds to the
size of the value of the depicted matrix. The colour determines, if the value is positive or negative. In our
example: the colour red denotes negative values and the colour green denotes positive values.)
NumPy is based on two earlier Python modules dealing with arrays. One of these is Numeric. Numeric is like
NumPy a Python module for high-performance, numeric computing, but it is obsolete nowadays. Another
predecessor of NumPy is Numarray, which is a complete rewrite of Numeric but is deprecated as well. NumPy
is a merger of those two, i.e. it is build on the code of Numeric and the features of Numarray.
8
When we say "Core Python", we mean Python without any special modules, i.e. especially without NumPy.
import numpy
But you will hardly ever see this. Numpy is usually renamed to np:
import numpy as np
Our first simple Numpy example deals with temperatures. Given is a list with values, e.g. temperatures in
Celsius:
cvalues = [20.1, 20.8, 21.9, 22.5, 22.7, 22.3, 21.8, 21.2, 20.9, 2
0.1]
C = np.array(cvalues)
print(C)
[20.1 20.8 21.9 22.5 22.7 22.3 21.8 21.2 20.9 20.1]
Let's assume, we want to turn the values into degrees Fahrenheit. This is very easy to accomplish with a
numpy array. The solution to our problem can be achieved by simple scalar multiplication:
print(C * 9 / 5 + 32)
[68.18 69.44 71.42 72.5 72.86 72.14 71.24 70.16 69.62 68.18]
9
The array C has not been changed by this expression:
print(C)
[20.1 20.8 21.9 22.5 22.7 22.3 21.8 21.2 20.9 20.1]
Compared to this, the solution for our Python list looks awkward:
So far, we referred to C as an array. The internal type is "ndarray" or to be even more precise "C is an instance
of the class numpy.ndarray":
type(C)
Output: numpy.ndarray
In the following, we will use the terms "array" and "ndarray" in most cases synonymously.
If you use the jupyter notebook, you might be well advised to include the following line of code to prevent an
external window to pop up and to have your diagram included in the notebook:
%matplotlib inline
The code to generate a plot for our values looks like this:
plt.plot(C)
plt.show()
10
The function plot uses the values of the array C for the values of the ordinate, i.e. the y-axis. The indices of the
array C are taken as values for the abscissa, i.e. the x-axis.
11
To calculate the memory consumption of the list from the above picture, we will use the function getsizeof
from the module sys.
The size of a Python list consists of the general list information, the size needed for the references to the
elements and the size of all the elements of the list. If we apply sys.getsizeof to a list, we get only the size
without the size of the elements. In the previous example, we made the assumption that all the integer
elements of our list have the same size. Of course, this is not valid in general, because memory consumption
will be higher for larger integers.
We will check now, how the memory usage changes, if we add another integer element to the list. We also
look at an empty list:
lst = []
print("Emtpy list size: ", size(lst))
12
Size without the size of the elements: 104
Size of all the elements: 112
Total size of list, including elements: 216
Emtpy list size: 72
We can conclude from this that for every new element, we need another eight bytes for the reference to the
new object. The new integer object itself consumes 28 bytes. The size of a list "lst" without the size of the
elements can be calculated with:
64 + 8 * len(lst)
To get the complete size of an arbitrary list of integers, we have to add the sum of all the sizes of the integers.
We will examine now the memory consumption of a numpy.array. To this purpose, we will have a look at the
implementation in the following picture:
We will create the numpy array of the previous diagram and calculate the memory usage:
We get the memory usage for the general array information by creating an empty array:
e = np.array([])
print(size(e))
96
13
We can see that the difference between the empty array "e" and the array "a" with three integers consists in 24
Bytes. This means that an arbitrary integer array of length "n" in numpy needs
96 + n * 8 Bytes
64 + 8 len(lst) + len(lst) 28
This is a minimum estimation, as Python integers can use more than 28 bytes.
When we define a Numpy array, numpy automatically chooses a fixed integer size. In our example "int64".
We can determine the size of the integers, when we define an array. Needless to say, this changes the memory
requirement:
import time
size_of_vec = 1000
def pure_python_version():
t1 = time.time()
14
X = range(size_of_vec)
Y = range(size_of_vec)
Z = [X[i] + Y[i] for i in range(len(X)) ]
return time.time() - t1
def numpy_version():
t1 = time.time()
X = np.arange(size_of_vec)
Y = np.arange(size_of_vec)
Z = X + Y
return time.time() - t1
t1 = pure_python_version()
t2 = numpy_version()
print(t1, t2)
print("Numpy is in this example " + str(t1/t2) + " faster!")
0.0010614395141601562 5.2928924560546875e-05
Numpy is in this example 20.054054054054053 faster!
It's an easier and above all better way to measure the times by using the timeit module. We will use the Timer
class in the following script.
The constructor of a Timer object takes a statement to be timed, an additional statement used for setup, and a
timer function. Both statements default to 'pass'.
The statements may contain newlines, as long as they don't contain multi-line string literals.
A Timer object has a timeit method. timeit is called with a parameter number:
timeit(number=1000000)
The main statement will be executed "number" times. This executes the setup statement once, and then returns
the time it takes to execute the main statement a "number" of times. It returns the time in seconds.
import numpy as np
from timeit import Timer
size_of_vec = 1000
X_list = range(size_of_vec)
Y_list = range(size_of_vec)
15
X = np.arange(size_of_vec)
Y = np.arange(size_of_vec)
def pure_python_version():
Z = [X_list[i] + Y_list[i] for i in range(len(X_list)) ]
def numpy_version():
Z = X + Y
for i in range(3):
t1 = timer_obj1.timeit(10)
t2 = timer_obj2.timeit(10)
print("time for pure Python version: ", t1)
print("time for Numpy version: ", t2)
print(f"Numpy was {t1 / t2:7.2f} times faster!")
time for pure Python version: 0.0021230499987723306
time for Numpy version: 0.0004346180066931993
Numpy was 4.88 times faster!
time for pure Python version: 0.003020321993972175
time for Numpy version: 0.00014882600225973874
Numpy was 20.29 times faster!
time for pure Python version: 0.002028984992648475
time for Numpy version: 0.0002098319964716211
Numpy was 9.67 times faster!
The repeat() method is a convenience to call timeit() multiple times and return a list of results:
print(timer_obj1.repeat(repeat=3, number=10))
print(timer_obj2.repeat(repeat=3, number=10))
[0.0030275019962573424, 0.002999588003149256, 0.002212086998042650
5]
[6.104000203777105e-05, 0.0001641790004214272, 1.904800592456013
e-05]
In [ ]:
16
NUMPY TUTORIAL: CREATING ARRAYS
ARANGE
The syntax of arange:
arange returns evenly spaced values within a given interval. The values are generated within the half-open
interval '[start, stop)' If the function is used with integers, it is nearly equivalent to the Python built-in function
range, but arange returns an ndarray rather than a list iterator as range does. If the 'start' parameter is not given,
it will be set to 0. The end of the interval is determined by the parameter 'stop'. Usually, the interval will not
include this value, except in some cases where 'step' is not an integer and floating point round-off affects the
length of output ndarray. The spacing between two adjacent values of the output array is set with the optional
parameter 'step'. The default value for 'step' is 1. If the parameter 'step' is given, the 'start' parameter cannot be
optional, i.e. it has to be given as well. The type of the output array can be specified with the parameter 'dtype'.
If it is not given, the type will be automatically inferred from the other input arguments.
import numpy as np
a = np.arange(1, 10)
print(a)
x = range(1, 10)
17
print(x) # x is an iterator
print(list(x))
Be careful, if you use a float value for the step parameter, as you can see in the following example:
The help of arange has to say the following for the stop parameter: "End of interval. The interval does
not include this value, except in some cases where step is not an integer and floating point round-off
affects the length of out . This is what happened in our example.
The following usages of arange is a bit offbeat. Why should we use float values, if we want integers as
result. Anyway, the result might be confusing. Before arange starts, it will round the start value, end value and
the stepsize:
This result defies all logical explanations. A look at help also helps here: "When using a non-integer step, such
as 0.1, the results will often not be consistent. It is better to use numpy.linspace for these cases. Using
linspace is not an easy workaround in some situations, because the number of values has to be known.
18
LINSPACE
The syntax of linspace:
linspace returns an ndarray, consisting of 'num' equally spaced samples in the closed interval [start, stop] or the
half-open interval [start, stop). If a closed or a half-open interval will be returned, depends on whether
'endpoint' is True or False. The parameter 'start' defines the start value of the sequence which will be created.
'stop' will the end value of the sequence, unless 'endpoint' is set to False. In the latter case, the resulting
sequence will consist of all but the last of 'num + 1' evenly spaced samples. This means that 'stop' is excluded.
Note that the step size changes when 'endpoint' is False. The number of samples to be generated can be set
with 'num', which defaults to 50. If the optional parameter 'endpoint' is set to True (the default), 'stop' will be
the last sample of the sequence. Otherwise, it is not included.
import numpy as np
19
We haven't discussed one interesting parameter so far. If the optional parameter 'retstep' is set, the function
will also return the value of the spacing between adjacent values. So, the function will return a tuple
('samples', 'step'):
import numpy as np
import numpy as np
x = np.array(42)
print("x: ", x)
print("The type of x: ", type(x))
print("The dimension of x:", np.ndim(x))
x: 42
The type of x: <class 'numpy.ndarray'>
The dimension of x: 0
ONE-DIMENSIONAL ARRAYS
We have already encountered a 1-dimenional array - better known to some as vectors - in our initial example.
What we have not mentioned so far, but what you may have assumed, is the fact that numpy arrays are
containers of items of the same type, e.g. only integers. The homogenous type of the array can be determined
with the attribute "dtype", as we can learn from the following example:
20
print("F: ", F)
print("V: ", V)
print("Type of F: ", F.dtype)
print("Type of V: ", V.dtype)
print("Dimension of F: ", np.ndim(F))
print("Dimension of V: ", np.ndim(V))
F: [ 1 1 2 3 5 8 13 21]
V: [ 3.4 6.9 99.8 12.8]
Type of F: int64
Type of V: float64
Dimension of F: 1
Dimension of V: 1
[[211 212]
[221 222]]
[[311 312]
[321 322]]]
3
21
SHAPE OF AN ARRAY
The function "shape" returns the shape of an array. The shape is a tuple of
integers. These numbers denote the lengths of the corresponding array
dimension. In other words: The "shape" of an array is a tuple with the number
of elements per axis (dimension). In our example, the shape is equal to (6, 3),
i.e. we have 6 lines and 3 columns.
print(np.shape(x))
(6, 3)
print(x.shape)
(6, 3)
The shape of an array tells us also something about the order in which the indices
are processed, i.e. first rows, then columns and after that the further dimensions.
x.shape = (3, 6)
print(x)
[[67 63 87 77 69 59]
[85 87 99 79 72 71]
[63 89 93 68 92 78]]
x.shape = (2, 9)
print(x)
22
[[67 63 87 77 69 59 85 87 99]
[79 72 71 63 89 93 68 92 78]]
You might have guessed by now that the new shape must correspond to the number of elements of the array,
i.e. the total size of the new array must be the same as the old one. We will raise an exception, if this is not the
case.
x = np.array(11)
print(np.shape(x))
()
print(B.shape)
(4, 2, 3)
Single indexing behaves the way, you will most probably expect it:
23
Indexing multidimensional arrays:
print(A[1][0])
1.1
We accessed an element in the second row, i.e. the row with the index 1, and the first column (index 0). We
accessed it the same way, we would have done with an element of a nested Python list.
You have to be aware of the fact, that way of accessing multi-dimensional arrays can be highly inefficient. The
reason is that we create an intermediate array A[1] from which we access the element with the index 0. So it
behaves similar to this:
tmp = A[1]
print(tmp)
print(tmp[0])
[ 1.1 -7.8 -0.7]
1.1
There is another way to access elements of multi-dimensional arrays in Numpy: We use only one pair of
square brackets and all the indices are separated by commas:
print(A[1, 0])
1.1
We assume that you are familar with the slicing of lists and tuples. The syntax is the same in numpy for one-
dimensional arrays, but it can be applied to multiple dimensions as well.
A[start:stop:step]
We illustrate the operating principle of "slicing" with some examples. We start with the easiest case, i.e. the
slicing of a one-dimensional array:
S = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
print(S[2:5])
print(S[:4])
print(S[6:])
24
print(S[:])
[2 3 4]
[0 1 2 3]
[6 7 8 9]
[0 1 2 3 4 5 6 7 8 9]
We will illustrate the multidimensional slicing in the following examples. The ranges for each dimension are
separated by commas:
A = np.array([
[11, 12, 13, 14, 15],
[21, 22, 23, 24, 25],
[31, 32, 33, 34, 35],
[41, 42, 43, 44, 45],
[51, 52, 53, 54, 55]])
print(A[:3, 2:])
[[13 14 15]
[23 24 25]
[33 34 35]]
print(A[3:, :])
[[41 42 43 44 45]
[51 52 53 54 55]]
25
print(A[:, 4:])
[[15]
[25]
[35]
[45]
[55]]
The following two examples use the third parameter "step". The reshape function is used to construct the two-
dimensional array. We will explain reshape in the following subchapter:
X = np.arange(28).reshape(4, 7)
print(X)
[[ 0 1 2 3 4 5 6]
[ 7 8 9 10 11 12 13]
[14 15 16 17 18 19 20]
[21 22 23 24 25 26 27]]
print(X[::2, ::3])
[[ 0 3 6]
[14 17 20]]
26
print(X[::, ::3])
[[ 0 3 6]
[ 7 10 13]
[14 17 20]
[21 24 27]]
If the number of objects in the selection tuple is less than the dimension N, then : is assumed
for any subsequent dimensions:
A = np.array(
[ [ [45, 12, 4], [45, 13, 5], [46, 12, 6] ],
[ [46, 14, 4], [45, 14, 5], [46, 11, 5] ],
[ [47, 13, 2], [48, 15, 5], [52, 15, 1] ] ])
27
Attention: Whereas slicings on lists and tuples create new objects, a slicing operation on an array creates a
view on the original array. So we get an another possibility to access the array, or better a part of the array.
From this follows that if we modify a view, the original array will be modified as well.
A = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
S = A[2:6]
S[0] = 22
S[1] = 23
print(A)
[ 0 1 22 23 4 5 6 7 8 9]
Doing the similar thing with lists, we can see that we get a copy:
lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
lst2 = lst[2:6]
lst2[0] = 22
lst2[1] = 23
print(lst)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
If you want to check, if two array names share the same memory block, you can use the function
np.may_share_memory.
np.may_share_memory(A, B)
To determine if two arrays A and B can share memory the memory-bounds of A and B are computed. The
function returns True, if they overlap and False otherwise. The function may give false positives, i.e. if it
returns True it just means that the arrays may be the same.
np.may_share_memory(A, S)
Output: True
The following code shows a case, in which the use of may_share_memory is quite useful:
A = np.arange(12)
B = A.reshape(3, 4)
A[0] = 42
print(B)
[[42 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
28
Discovering Diverse Content Through
Random Scribd Documents
on their first evening, in her first evening dress, swished her way in
and out of the dining-tables, very grown-up and shy and
uncomfortable. Mrs. Cloud would not have changed Justin for a
dozen Lauras, and yet, watching her entry, quite alive to the heads
that turned, and the murmur at the nearer tables, she wished she
had a beautiful young daughter of her own of whom to be critically
proud.
“Green’s your colour!” said Mrs. Cloud, as Laura settled herself. No
more—but it was the accolade.
Laura blushed and glanced at Justin.
“Chianti or white wine?” he enquired with some interest.
“No, thank you. Water, please.” (... Men were queer!)
“Oh, if you’d rather!” (... Odd things, women!)
It was the last straw when Art, the Italian jade, plucked at Justin’s
sleeve, whispering that two were company ... and Justin went out to
Pavia all by himself. Mrs. Cloud had a headache. Laura, because she
felt like it, spent her afternoon at the Campo Santo, and, among
tombs, made up her mind to have it out with Justin.
She had a certain desperate directness in emergencies that might
easily have been mistaken for courage. She had quite the average
capacity of a woman for subterfuge, but, linked with it, a curious
dread of being spared in her turn. She could face an ugly truth, but
she could not endure it tailored. She must know where she stood.
She must know where she stood with Justin, risking snubs; though
she dreaded being snubbed as only soft-shelled youth can. She must
know what she done wrong. She was quite sure that, whatever it
was, it was her fault, because if it were not her fault, it would be
Justin’s.... And that was impossible.... She did not pretend to
understand Justin, she knew she was not clever enough for that, but
at least she realized that he had no faults.... She was not quite a
fool.... There were certain inexplicabilities, of course, but they were
not her presumptuous business....
One does not criticize one’s god, or only when one has ceased to
believe in him. But God is not God when one ceases to believe in
Him.
She attacked Justin the next evening, choosing the wrong
moment, when he was tired, ready for a pipe and a book rather than
argument. But he had been kind to her at dinner and she had made
him laugh. (At least she could always make him laugh.) She thought
his mood could not change in half an hour.
But it had changed. He was absorbed, if not somnolent: had not a
glance to spare as she hesitated in front of him.
“Justin? Aren’t you coming out again?”
He shook his head.
She looked out of the window. The moon glimmered in the white
sky, thin and flat and unsubstantial, like a peeled honesty leaf: and,
below, the square was glamorous. The cathedral that rose out of it,
like June woods turned to stone, quivered in the warm dusk as on
the verge of disenchantment. The dots of lamp-light increased like
buttercups all opening at once, and among them people moved in
vague masses. A shrill of voices and laughter floated upwards.
Laura turned to Justin, straining his eyes over Baedeker’s Northern
Italy. The sight of the crowd had stirred her, made her want to go
down into it, just as the sight of the sea makes you want to bathe.
“It’s only half-past eight,” she hazarded.
He read on.
She glanced across at Mrs. Cloud, half asleep at the other end of
the huge deserted hotel sitting-room. They were the only people
indoors on that warm spring night of Italy.
Suddenly she attacked him—
“Justin, you’ll hurt your eyes.” Then, with a curtness that was pure
embarrassment, “Justin, what’s the matter?”
“The matter?” He raised his eyebrows.
“Yes. I want to know.” She hesitated. “Is anything wrong? Have I
done anything you don’t like? What makes you——?”
“What?”
“Oh, I don’t know—so funny to me. So—grumpy.”
“I’m sorry. I didn’t know——” he began stiffly.
She flared out.
“Of course you know. It’s been perfectly awful. You sit on me and
sit on me—and go out by yourself—and fidget at meals when I talk
——”
“I say, don’t wake Mother,” he warned her.
Hastily she dropped an octave.
“So I think you might tell me what’s the matter,” she concluded.
“Oh rot, Laura,” said Justin uncomfortably. “What should be the
matter?”
He waited a moment for her answer; but she said nothing: was
waiting in her turn. He looked at his book.
If he once began reading again....
“I don’t know,” she said hastily, “but there is. You might tell me,
Justin.” She put her hand upon his open book, would not budge as
he tried politely to move it. “You’ve got to tell me,” she insisted.
It was a very young and ignorant thing to do, crudely provocative
if it had not been so utterly unconscious. A woman or an older man
would have laughed and understood and found it charming enough.
But it annoyed Justin. He hated to be bothered. He had a keen
sense of his own dignity. Above all he had a horror of being inveigled
into anything approaching sentimentality. And he was out of touch
with Laura. He had been prepared for a jolly little girl, not for a
young woman with obvious faults and disconcerting garments. He
was just too old to label her challenge ‘cheek,’ yet not old enough to
make allowances for her hobble-de-hoyhood, to differentiate
between impudence and a lack of savoir-faire. Ever since Lucerne he
had been, though he had no idea of analysing his attitude,
disappointed, on the edge of boredom. He was as unaware as she
herself of the beauty of her hand, he merely knew that he didn’t
want a great paw sprawling over his book. He wanted to say “Get
out!” And she stood there and waited!
He leaned back in his chair with elaborate indifference.
“Justin?”
She was actually smiling at him—pleased, he supposed, with the
success of her idiotic performance.
“I don’t know that it’s anything much,” he was impelled to begin.
“It doesn’t matter anyway. It’s only——” He broke off.
“Tell me,” she insisted. And again he disliked her tone. Who was
she to order him about? Oh, well, if she wanted it she should have
it....
“You’re rather different from what I expected.” He stopped. It was
not perfectly easy, annoyed as he was.
“How?”
“Oh, I don’t know.”
“How?” She had a touch of colour in her cheek. Her bright eyes
compelled him.
“You’re—rather French, you know. You don’t seem quite—natural.”
“How?”
“Well, your clothes——”
Her face fell.
“Oh, Justin, don’t you like them?”
“They’re rather bright.”
“Oh!”
He did not volunteer anything.
“What else, Justin?”
“Oh, how do I know?” He was impatient. “It’s not my business.
But I hate scent and chatter and high heels and things that jingle.
And you come down to dinner with your hair fussed out like an
actress. But it’s all right, I expect.”
“I see.” She managed to smile at him before she swished across to
the window, with the little un-English swing of her body that was
another of her ways that vaguely irritated him. He made an
impatient movement. Of course he didn’t want to hurt her feelings,
but why on earth did she worry him?
“I only mean——You wouldn’t see Mother——Every one looks at
you!” And then, “I’m sorry, Laura, but you made me say what I
think.”
“Of course. I’m glad. I’m glad to know what you think.”
Her voice grew higher and higher as she tried to over-top the
catch in it. He had put a match to her quick young pride, and it
blazed and raged within her till she was quite sick with the physical
pain of it. The intolerable, humiliating tears rose under her lids.
Always with her back to him she took her handkerchief, screwed it to
a point, and removed them with precise care. She could not quite
control them, the square danced mistily, but at least she would not
show a stained face. Head up before everything!
‘Not natural,’ ‘like an actress.’... Oh, it wasn’t fair of Justin ...
wasn’t fair not to give her time to get used to him again.... He’d
been grown-up so much longer, but didn’t he remember what it felt
like to be shy and awkward and uncertain?... How could one cover it
up but by being glib?... At Paris they liked her.... Mrs. Cloud liked
her.... Mrs. Cloud had liked her green dress.... She didn’t know what
he meant.... It wasn’t vanity, everybody waved their hair.... She
couldn’t help her voice being loud.... She had never realized that she
was so full of faults.... She had only wanted to make herself nice—
and now it was all wrong.... And after looking forward so to Italy....
Not that she cared ... not that she cared a hang!...
“Don’t worry, Laura!” Justin was stirred by a vague compunction,
though he wished that she did not find it necessary to stand
between him and the last of the light. “What does it matter? I told
you—it’s nothing to do with me.”
She whirled round indignantly, all eyes and flame.
“Whom else has it got to do with but you and Mrs. Cloud and
Gran’papa? If you feel that way I’ve got to alter things. It’s dreadful!
It’s dreadful that you don’t like me any more.”
He was obliged to smile at that—a smile that lit up his face as
sunshine brightens a room: and suddenly, for the first time since
their meeting, he was at home with her again. The simplicity of her
passionate distress was so familiar, so entirely the Laura he had
missed, that the two alienating years were blotted out, as the
darkness was blotting out Laura’s skirts and offending airs and
graces, leaving him his foundling again in one of her tragi-comic
rages, his rum old Laura, raw from conflict with life and Aunt Adela.
She must be smoothed down!... She must be smoothed down at
once!...
“Here, dry up, Laura,” he advised her, “and don’t talk so much.
You’re right, it’s getting too dark to read. Come on out with me and
eat spaghetti on the pavement. They say that’s the thing to do when
there’s a moon.”
For an open-mouthed moment she stared at him: then, with a
comprehension of his change of attitude that was uncanny,
controlled herself, controlled her choking need of a good cry, nodded
cheerfully, and ran upstairs for her hat, her old straw hat at the
bottom of her trunk that she had not meant to wear in Italy.
It was going to be all right.... He was going to understand.... He
was going to be himself again ... if she only kept quiet and wore her
old clothes.... Oh, all ye works of the Lord, bless ye the Lord!... She
dashed downstairs.
It was a cloudless night. The macaroni was delicious. The clang of
the trams was like Eastern music. Laura was quiet and sweet. Justin
found that he was enjoying himself, and was moved to tell all about
his tour around the world, and she was deeply interested and asked
extraordinarily intelligent questions, and there was no shadow upon
them any more, save the shadow of the great cathedral, black and
white and wonderful under the moon.
It was late when they came back to an amused, forsaken Mrs.
Cloud, and were eloquent for half an hour upon moonlight and
macaroni and Milan.
And Justin said good-night to Laura and shook hands with her
properly instead of grunting off to bed as he generally did. He said
she was to sleep well. She said she would.
Yet the dawn a few hours later, nosing damply in between
venetian blinds, surprised Laura, with wet brushes and a determined
mouth, still hard at work before her looking-glass, brushing,
brushing, brushing the vanity out of her splendid hair.
CHAPTER XIV