Professional Documents
Culture Documents
Advanced Data Analytics Using Python With Architectural Patterns Text and Image Classification and Optimization Techniques 2Nd Edition Sayan Mukhopadhyay Full Chapter
Advanced Data Analytics Using Python With Architectural Patterns Text and Image Classification and Optimization Techniques 2Nd Edition Sayan Mukhopadhyay Full Chapter
Pratip Samanta
Kolkata, West Bengal, India
Apress Standard
The publisher, the authors and the editors are safe to assume that the
advice and information in this book are believed to be true and accurate
at the date of publication. Neither the publisher nor the authors or the
editors give a warranty, express or implied, with respect to the material
contained herein or for any errors or omissions that may have been
made.
Pratip Samanta
is a principal AI engineer/researcher
with more than 11 years of experience.
He has worked for several software
companies and research institutions. He
has published conference papers and has
been granted patents in AI and natural
language processing. He is also
passionate about gardening and
teaching.
About the Technical Reviewer
Joos Korstanje
is a data scientist with more than five
years of industry experience in
developing machine learning tools, of
which a large part is forecasting models.
He currently works at Disneyland Paris
where he develops machine learning for
a variety of tools.
© Sayan Mukhopadhyay, Pratip Samanta 2023
S. Mukhopadhyay, P. Samanta, Advanced Data Analytics Using Python
https://doi.org/10.1007/978-1-4842-8005-8_1
In this book, we assume that you are familiar with Python programming. In this introductory chapter, we
explain why a data scientist should choose Python as a programming language. Then we highlight some
situations where Python may not be the ideal choice. Finally, we describe some best practices for application
development and give some coding examples that a data scientist may need in their day-to-day job.
OOP in Python
In this section, we explain some features of object-oriented programming (OOP) in a Python context.
The most basic element of any modern application is an object. To a programmer or architect, the world
is a collection of objects. Objects consist of two types of members: attributes and methods. Members can be
private, public, or protected. Classes are data types of objects. Every object is an instance of a class. A class
can be inherited in child classes. Two classes can be associated using composition.
Python has no keywords for public, private, or protected, so encapsulation (hiding a member from the
outside world) is not implicit in Python. Like C++, it supports multilevel and multiple inheritance. Like Java,
it has an abstract keyword. Classes and methods both can be abstract.
In the following code, we are describing an object-oriented question-answering system without any
machine learning. The program’s input is a set of dialogs in input.txt, as shown here:
glob is I
prok is V
pish is X
tegj is L
glob glob Silver is 34 Credits
glob prok Gold is 57800 Credits
pish pish Iron is 3910 Credits
how much is pish tegj glob glob ?
how many Credits is glob prok Silver ?
how many Credits is glob prok Gold ?
how many Credits is glob prok Iron ?
how much wood could a woodchuck chuck if a woodchuck could chuck wood?
Program has a knowledge base in config.txt.
I,1,roman
V,5,roman
X,10,roman
L,50,roman
C,100,roman
D,500,roman
M,1000,roman
Based on this input and the configuration program, the answer to the question is given in input.txt in
standard output, as shown here:
pish tegj glob glob is 42
glob prok Silver is 68 Credits
glob prok Gold is 57800 Credits
glob prok Iron is 782 Credits
I have no idea what you are talking about
The parsing logic is in the Observer class.
import operator
def __init__(self,cmpiler):
self.compiler = cmpiler
def evaluate(self):
#check mximum repeatation is crossing the limit
if self.count > 3:
raise Exception("Repeat more than 3")
#symbol is proper or not
if self.symbol not in self.compiler.symbol_map:
raise Exception("Wrong Symbol")
#check if wrong symbol is repeated ie (V, ..
self.symbol,unit = self.compiler.evaluateSymbol(self.symbol)
while self.symbol % 10 != 0:
self.symbol = self.symbol / 10
if self.count > 1 and self.symbol == 5:
raise Exception("Wrong Symbol repeated")
#checking if input sentence is proper or not
def evaluateSentence(self, line):
if "is" not in line:
return "I have no idea what you are talking about"
The compilation logic is in the compiler class, as shown here:
import sys
class compilerTrader(object):
#store mapping of symbols with score and unit
symbol_map = {}
#store the list of valid symbol
valid_values = []
import sys
class answeringTrader(compilerTrader):
obs.calculate()
obs.evaluate()
values = values.replace("?" , "is ")
if unit == 'roman':
unit = ''
return(values + str(ans) + ' ' + unit)
Finally, the main program calls the answering class and the observer, and then it performs the task
and does unit testing on the logic.
import sys
import unittest
sys.path.append('./answerLayer')
sys.path.append('./compilerLayer')
sys.path.append('./utilityLayer')
def setUp(self):
pass
if __name__ == '__main__':
if len(sys.argv) != 3:
print("Usage is : " + sys.argv[0] + " <intput file
path> <config file path>")
exit(0)
tr = ClientTrader(sys.argv[2])
f = open(sys.argv[1])
for line in f:
response = tr.process(line.strip())
if response is not None:
print(response)
TestTrader.trader = tr
unittest.main(argv = [sys.argv[0]], exit = False)
You can run this program with the following command:
import rpy2.robjects as ro
ro.r('data(input)')
ro.r('x <-HoltWinters(input data frame)')
import subprocess
subprocess.call(['java','-
cp','*','edu.stanford.nlp.sentiment.SentimentPipeline','-file','foo.txt'])
Please place foo.txt in the same folder where you run the Python code.
You can expose Stanford NLP as a web service and call it as a service. (Before running this code, you’ll
need to download the Stanford nlp JAR file available with the book’s source code.)
nlp = StanfordCoreNLP('http://127.0.0.1:9000')
output = nlp.annotate(sentence, properties={
"annotators": "tokenize,ssplit,parse,sentiment",
"outputFormat": "json",
# Only split the sentence at End Of Line. We assume that this method only
takes in one single sentence.
"ssplit.eolonly": "true",
# Setting enforceRequirements to skip some annotators and make the process
faster
"enforceRequirements": "false"
})
import falcon
from falcon_cors import CORS
import json
from sqlalchemy import *
from sqlalchemy.orm import sessionmaker
import pygeoip
from pymongo import MongoClient
import json
import datetime as dt
import ipaddress
import math
from concurrent.futures import *
from sqlalchemy.engine import Engine
from sqlalchemy import event
import sqlite3
@event.listens_for(Engine, "connect")
def set_sqlite_pragma(dbapi_connection, connection_record):
cursor = dbapi_connection.cursor()
cursor.execute("PRAGMA cache_size=100000")
cursor.close()
class Predictor(object):
def __init__(self,domain):
db1 = create_engine('sqlite:///score_' + domain + '0test.db')
metadata1 = MetaData(db1)
self.scores = Table('scores', metadata1, autoload=True)
client = MongoClient(connect=False,maxPoolSize=1)
self.db = client.frequency
self.gi = pygeoip.GeoIP('GeoIP.dat')
self.high = 1.2
self.low = .8
def get_hour(self,timestamp):
return dt.datetime.utcfromtimestamp(timestamp / 1e3).hour
def get_score(self, featurename, featurevalue):
pred = 0
s = self.scores.select((self.scores.c.feature_name ==
featurename) & (self.scores.c.feature_value == featurevalue))
rs = s.execute()
row = rs.fetchone()
if row is not None:
pred = pred + float(row['score'])
res = self.db.frequency.find_one({"ip" : ip})
freq = 1
if res is not None:
freq = res['frequency']
pred2, prob2 = self.get_score('frequency', str(freq))
return (pred1 + pred2), (prob1 + prob2)
conn = sqlite3.connect('multiplier.db')
cursor = conn.execute("SELECT high,low from multiplier
where domain='" + value + "'")
row = cursor.fetchone()
if row is not None:
self.high = row[0]
self.low = row[1]
return self.get_score(f, value)
def on_post(self, req, resp):
input_json = json.loads(req.stream.read(),encoding='utf-8')
input_json['ip'] = unicode(req.remote_addr)
pred = 1
prob = 1
with ThreadPoolExecutor(max_workers=8) as pool:
future_array = {
pool.submit(self.get_value,f,input_json[f]) : f for f in input_json}
for future in as_completed(future_array):
pred1, prob1 = future.result()
pred = pred + pred1
prob = prob - prob1
resp.status = falcon.HTTP_200
res = math.exp(pred)-1
if res < 0:
res = 0
prob = math.exp(prob)
if(prob <= .1):
prob = .1
if(prob >= .9):
prob = .9
multiplier = self.low + (self.high -self.low)*prob
pred = multiplier*pred
resp.body = str(pred)
cors =
CORS(allow_all_origins=True,allow_all_methods=True,allow_all_headers=True)
wsgi_app = api = falcon.API(middleware=[cors.middleware])
f = open('publishers1.list')
for domain in f:
domain = domain.strip()
p = Predictor(domain)
url = '/predict/' + domain
api.add_route(url, p)
Having covered design patterns in Python a bit, let’s now take a look at some essential architecture
patterns for data scientists.
Summary
In this chapter, we discussed fundamental engineering principles for data scientists, which are covered in
separate chapters. The question-answering example can help you understand how to organize your code.
The basic rule is to not put everything into one class. Divide your code into many categories and use parent-
child relationships where they exist. Then you learned how to use Python to call other languages’ code. We
provided two instances of R and Java code calls. Then we showed you how to expose your model as a REST
API and make it perform well by using concurrent programming. Following that, we covered significant
architectural patterns from data scientists.
© Sayan Mukhopadhyay, Pratip Samanta 2023
S. Mukhopadhyay, P. Samanta, Advanced Data Analytics Using Python
https://doi.org/10.1007/978-1-4842-8005-8_2
Every data science professional has to extract, transform, and load (ETL) data from different data
sources. In this chapter, we will discuss how to perform ETL with Python for a selection of
popular databases. For a relational database, we’ll cover MySQL. As an example of a document
database, we will cover Elasticsearch. For a graph database, we’ll cover Neo4j, and for NoSQL,
we’ll cover MongoDB. We will also discuss the Pandas framework, which was inspired by R’s data
frame concept.
ETL is based on a process in which data is extracted from multiple sources, transformed into
specific formats that involve cleaning enrichment, and finally loaded into its target destination.
The following are the details of each process:
1. Extract: During data extraction, source data is pulled from a variety of sources and moved to a
staging area, making the data available to subsequent stages in the ETL process. After that,
the data undergoes the cleaning and enrichment stage, also known as data cleansing.
2. Transform: In this stage, the source data is matched to the format of the target system. This
includes steps such as changing data types, combining fields, splitting fields, etc.
3. Load: This stage is the final ETL stage. Here, data is loaded into the data warehouse in an
automated manner and can be periodically updated. Once completed, the data is ready for
data analysis.
The previous processes are important in any data analytics work. Once the data goes through
the ETL processes, then it becomes possible to analysis the data, find insights, and so on.
We will discuss various types of ETL throughout this chapter. We discussed in Chapter 1 that
data is not an isolated thing. We need to load data from somewhere, which is a database. We need
to fetch the data from some application, which is extraction. In this chapter and the next, we will
discuss various feature engineering that transforms the data from one form to another.
MySQL
MySQLdb is an API in Python developed to work on top of the MySQL C interface.
#!/usr/bin/python
import MySQLdb
If you get an import error exception, that means the module was not installed properly.
The following are the instructions to install the MySQL Python module:
$ gunzip MySQL-python-1.2.2.tar.gz
$ tar -xvf MySQL-python-1.2.2.tar
$ cd MySQL-python-1.2.2
$ python setup.py build
$ python setup.py install
Database Connection
Before connecting to a MySQL database, make sure you do the following:
1. You need to access a database called TEST with the sql "use test" command.
2. In TEST you need a table named STUDENT; use the command sql "create table
student(name varchar(20), sur_name varchar(20),roll_no int");.
4. There needs to be a user in TEST that has complete access to the database.
If you do not do these steps properly, you will get an exception in the next Python code.
INSERT Operation
The following code carries out the SQL INSERT statement for the purpose of creating a record in
the STUDENT table:
#!/usr/bin/python
import MySQLdb
# Open database connection
db = MySQLdb.connect("localhost","user","passwd","TEST" )
# prepare a cursor object using cursor() method
cursor = db.cursor()
# Prepare SQL query to INSERT a record into the database.
sql = """INSERT INTO STUDENT(NAME,
SUR_NAME, ROLL_NO)
VALUES ('Sayan', 'Mukhopadhyay', 1)"""
try:
# Execute the SQL command
cursor.execute(sql)
# Commit your changes in the database
db.commit()
except:
# Rollback in case there is any error
db.rollback()
# disconnect from server
db.close()
READ Operation
The following code fetches data from the STUDENT table and prints it:
#!/usr/bin/python
import MySQLdb
# Prepare SQL query to INSERT a record into the database.
sql = "SELECT * FROM STUDENT "
try:
# Execute the SQL command
cursor.execute(sql)
# Fetch all the rows in a list of lists.
results = cursor.fetchall()
for row in results:
fname = row[0]
lname = row[1]
id = row[2]
# Now print fetched result
Print( "name=%s,surname=%s,id=%d" % \
(fname, lname, id ))
except:
print "Error: unable to fecth data"
DELETE Operation
The following code deletes a row from TEST with id=1:
#!/usr/bin/python
import MySQLdb
# Prepare SQL query to DELETE required records
sql = "DELETE FROM STUDENT WHERE ROLL_NO =1"
try:
# Execute the SQL command
cursor.execute(sql)
# Commit your changes in the database
db.commit()
except:
# Rollback in case there is any error
db.rollback()
UPDATE Operation
The following code changes the lastname variable to Mukherjee, from Mukhopadhyay:
#!/usr/bin/python
import MySQLdb
# Prepare SQL query to UPDATE required records
sql = "UPDATE STUDENT SET SUR_NAME="Mukherjee"
WHERE SUR_NAME="Mukhopadhyay"
try:
# Execute the SQL command
cursor.execute(sql)
# Commit your changes in the database
db.commit()
except:
# Rollback in case there is any error
db.rollback()
COMMIT Operation
The commit operation provides its assent to the database to finalize the modifications, and after
this operation, there is no way that this can be reverted.
ROLL-BACK Operation
If you are not completely convinced about any of the modifications and you want to reverse them,
then you can apply the roll-back() method.
The following is a complete example of accessing MySQL data through Python. It will give the
complete description of the data stored in a CSV file or MySQL database.
This code asks for the data source type, either MySQL or text. For example, if MySQL asks for
the IP address, credentials, and database name and shows all tables in the database, it offers its
fields once the table is selected. Similarly, a text file asks for a path, and in the files it points to, all
the columns are shown to the user.
import MySQLdb
import sys
out = open('Config1.txt','w')
print ("Enter the Data Source Type:")
print( "1. MySql")
print ("2. Exit")
while(1):
data1 = sys.stdin.readline().strip()
if(int(data1) == 1):
out.write("source begin"+"\n"+"type=mysql\n")
Normal Forms
Database normal forms are the principles to organize your data in an optimum way.
Every table in a database can be in one of the normal forms that we’ll go over next. For the
primary key (PK) and foreign key (FK), you want to have as little repetition as possible. The rest of
the information should be taken from other tables.
First normal form (1NF)
Second normal form (2NF)
Third normal form (3NF)
1. The recurring group in the Student Grade Report table contains the course information. A
student can enroll in a variety of courses.
2. Get rid of the group that keeps repeating itself. That’s each student’s course information in
this situation.
4. The attribute value must be identified uniquely by the PK (StudentNo and CourseNo).
2. When looking at the Student Course table, you can observe that not all of the characteristics,
especially the course details, are completely dependent on the PK. The grade is the sole
attribute that is entirely reliant on xxx.
3. Inspect new and updated tables to ensure that each table has a determinant and that no
tables have improper dependencies.
There should be no abnormalities in the third normal form at this point. For this example,
consider the dependency diagram in Figure 2-1. As previously said, the first step is to eliminate
repeated groupings.
Review the dependencies in Figure 2-1, which summarizes the normalization procedure for
the School database.
The following are the abbreviations used in Figure 2-1:
PD stands for partially dependent.
TD stands for transitive dependence.
FD stands for full dependency. (FD stands for functional dependence in most cases. Figure 2-1
is the only place where FD is used as an abbreviated form for full dependence.)
A relational database is valuable when structured data and a strict relationship between the
fields are maintained. But what if you do not have structured data in which a strict relationship
between fields has been maintained? That’s where Elasticsearch comes in.
Elasticsearch
You’ll find that data is often unstructured. Meaning, you may end up with a mix of image data,
sensor data, and other forms of data. To analyze this data, we first need to store it. MySQL or SQL-
based databases are not good at storing unstructured data. So here we introduce a different kind
of storage, which is mainly used to handle unstructured textual data.
Elasticsearch is a Lucene-based database, which makes it is easy to store and search text data.
Its query interface is a REST API endpoint. The Elasticsearch (ES) low-level client gives a direct
mapping from Python to ES REST endpoints. One of the big advantages of Elasticsearch is that it
provides a full-stack solution for data analysis in one place. Elasticsearch is the database. It has a
configurable front end called Kibana, a data collection tool called Logstash, and an enterprise
security feature called Shield.
This example has features called cat, cluster, indices, ingest, nodes, snapshot, and tasks that
translate to instances of CatClient, ClusterClient, IndicesClient, CatClient,
ClusterClient, IndicesClient, IngestClient, NodesClient, SnapshotClient,
NodesClient, SnapshotClient, and TasksClient, respectively. These instances are the
only supported way to get access to these classes and their methods.
You can specify your own connection class, which can be used by providing the
connection_class parameter.
Different hosts can have different parameters (hostname, port number, SSL option); you can
use one dictionary per node to specify them.
Es1=Elasticsearch(
['localhost:443','other_host:443'],
# turn on SSL
use_ssl=True,
# make sure we verify SSL certificates (off by default)
verify_certs=True,
# provide a path to CA certs on disk
ca_certs='path to CA_certs',
# PEM formatted SSL client certificate
client_cert='path to clientcert.pem',
# PEM formatted SSL client key
client_key='path to clientkey.pem'
)
neo4j-rest-client
The main objective of neo4j-rest-client is to make sure that the Python programmers already
using Neo4j locally through python-embedded are also able to access the Neo4j REST server. So,
the structure of the neo4j-rest-client API is completely in sync with python-embedded. But, a new
structure is brought in so as to arrive at a more Pythonic style and to augment the API with the
new features being introduced by the Neo4j team.
In-Memory Database
Another important class of databases is an in-memory database. This type stores and processes
the data in RAM. So, operations on the database are fast, and the data is volatile. SQLite is a
popular example of an in-memory database. In Python you need to use the sqlalchemy library to
operate on SQLite. In Chapter 1’s Flask and Falcon example, I showed you how to select data from
SQLite. Here I will show how to store a Pandas data frame in SQLite:
{
"_id":ObjectId("01"),
"address": {
"street":"Siraj Mondal Lane",
"pincode":"743145",
"building":"129",
"coord": [ -24.97, 48.68 ]
},
"borough":"Manhattan",
Client11 = MongoClient("mongodb://myhostname:27017")
Db11 = client11.primer
db11 = client11['primer']
Collection objects can be accessed directly by using the dictionary style or the attribute access
from a database object, as shown in the following two examples:
Coll11 = db11.dataset
coll = db11['dataset']
Insert Data
You can place a document into a collection that doesn’t exist, and the following operation will
create the collection:
Update Data
Here is how to update data:
result=db.address.update_one(
{"building": "129",
{"$set": {"address.street": "MG Road"}}
)
Remove Data
To expunge all documents from a collection, use this:
result=db.restaurants.delete_many({})
Cloud Databases
Even though the cloud has its own chapter, we’d like to provide you with an overview of cloud
databases, particularly databases for large data. People prefer cloud databases when they want
their systems to scale automatically. Google Big Query is the greatest tool for searching your data.
Azure Synapsys has a similar feature; however, it is significantly more expensive. You can store
data on S3, but if you want to run a query, you’ll need Athena, which is expensive. So, in modern
practice, data is stored as a blob in S3, and everything is done in a Python application. If there is
an error in data finding, this method takes a long time. Amazon Redish can also handle a
considerable quantity of large data and comes with a built-in BI tool.
Pandas
The goal of this section is to show some examples to enable you to begin using Pandas. These
illustrations have been taken from real-world data, along with any bugs and weirdness that are
inherent. Pandas is a framework inspired by the R data frame concept.
Please find the CSV file at the following link:
https://github.com/Apress/advanced-data-analytics-python-2e
import pandas as pd
broken_df=pd.read_csv('fetaure_engineering_data.csv')
broken_df[:3]
There are many other methods such as sort, groupby, and orderby in Pandas that are
useful when playing with structured data. Also, Pandas has a ready-made adapter for popular
databases such as MongoDB, Google Big Query, and so on.
One complex example with Pandas is shown next. In the X data frame for each distinct column
value, find the average value of the floor grouping by the root column.
Email Parsing
See Chapter 1 for a complete example of web crawling using Python.
Like Beautiful Soup, Python has a library for email parsing. The following is the example code
to parse email data stored on a mail server. The inputs in the configuration are the username and
number of mails to parse for the user.
In this code, you have to mention the email user, email folder, and index of the mail-in config;
code will write from the address to handle the subject and the date of the email in the CSV file.
conf = open(sys.argv[1],'w')
conf.write("path=" + config["path"] + "\n")
conf.write("folder=" + config["folder"] + "\n")
for usr in users.keys():
conf.write("name="+ usr +",value=" + users[usr] + "\n")
conf.close()
path=/cygdrive/c/share/enron_mail_20110402/enron_mail_20110402/maildir
folder=Inbox
name=storey-g,value=142
name=ybarbo-p,value=775
name=tycholiz-b,value=602
Topical Crawling
Topical crawlers are intelligent crawlers that retrieve information from anywhere on the Web.
They start with a URL and then find links present in the pages under it; then they look at new
URLs, bypassing the scalability limitations of universal search engines. This is done by
distributing the crawling process across users, queries, and even client computers. Crawlers can
use the context available to infinitely loop through the links with a goal of systematically locating
a highly relevant, focused page.
Web searching is a complicated task. A large chunk of machine learning work is being applied
to find the similarity between pages, such as the maximum number of URLs fetched or visited.
Crawling Algorithms
Figure 2-2 describes how the topical crawling algorithm works with its major components.
Figure 2-2 Topical crawling described
The starting URL of a topical crawler is known as the seed URL. There is another set of URLs
known as the target URLs, which are examples of desired output.
Another intriguing application of crawling is for a startup that wants to uncover crucial
keywords for every IP address. In the HTTP packet header, they acquire the user’s browsing
history from the Internet service provider. After crawling the URL visited by that IP, they classify
the words in the text using name-entity recognition (Stanford NLP), which is easily
implementable by the RNN explained in Chapter 5. All name entities and their types, such as
names of people, locations, and organizations, are recommended for the user.
import requests
from bs4 import BeautifulSoup
import nltk
from nltk.tokenize import word_tokenize
from nltk.corpus import stopwords
from nltk.tag import StanfordNERTagger
import re
import json
import os
import socket
import struct
def ip2int(addr):
return struct.unpack("!I", socket.inet_aton(addr))[0]
def int2ip(addr):
return socket.inet_ntoa(struct.pack("!I", addr))
java_path = '/usr/bin/java'
os.environ['JAVAHOME'] = java_path
os.environ['STANFORD_MODELS'] = '/home/ec2-user/stanford-ner.jar'
nltk.internals.config_java(java_path)
f = open("/home/ec2-user/data.csv")
res = []
stop_words = set(stopwords.words('english'))
for line in f:
fields = line.strip().split(",")
url = fields[1]
ip = fields[-1]
print(ip)
print(url)
tags_del = None
if True:
try:
ip = ip2int(ip)
except:
continue
print(ip)
tagged = None
try:
code = requests.get(url)
plain = code.text
s = BeautifulSoup(plain)
tags_del = s.get_text()
if tags_del is None:
continue
no_html = re.sub('<[^>]*>', '', tags_del)
st = StanfordNERTagger('/home/ec2-
user/english.all.3class.distsim.crf.ser.gz',
'/home/ec2-user/stanford-ner.jar')
tokenized = word_tokenize(no_html)
tagged = st.tag(tokenized)
except:
pass
if tagged is None:
continue
for t in tagged:
t = list(t)
t[0] = t[0].replace(' ', '')
t[-1] = t[-1].replace(' ', '')
print(t)
if t[0] in stop_words:
continue
unit = {}
unit["ip"] = ip
unit["word"] = t[0]
unit["name_entity"] = t[-1]
res.append(unit)
res_final = {}
res_final["result"] = res
#except:
# pass
#except:
# pass
Summary
In this chapter, we discussed different kind of databases and their use cases, and we discussed
collecting text data from the Web and extracting information from different types of unstructured
data like email and web pages.
Another random document with
no related content on Scribd:
On the 26th, about half an hour after sunset, arrived at Ghraat;
and were soon visited by a number of Hateeta’s relations, one of
whom was his sister. Some were much affected, and wept at the
sufferings that had detained him so long from them. A number of his
male relations soon came, and many of the inhabitants of the town.
The ladies were a free and lively set. They were not a little pleased
with the grave manner we uttered the various complimentary
expressions. Hateeta was not well pleased with something that he
had heard. He told us not to be afraid; for he had numerous
relations. We said that fear never entered our breasts, and begged
him not to be uneasy on our account.
Saturday, July 27. Early in the morning numerous visitors paid
their respects to Hateeta, and were introduced in due form to us. We
felt the length of time spent in salutations quite fatiguing, and so
absurd in our eyes, that we could scarcely at times retain our gravity.
Our visitors were mostly residents of the city, and all were decorated
in their best. There was a sedateness and gravity in the appearance
of all, which the dress tended greatly to augment. There were three
natives of Gadames, one of whom knew us through our worthy friend
Mahomet D’Ghies; another we had seen in the house of Dr. Dickson;
and the third was well acquainted with European manners, having
been over at Leghorn.
In the afternoon we visited the sultan. Mats had been spread in
the castle, in a small ante-chamber. The old man was seated, but
rose up to receive us and welcome us to his city. He apologized for
not waiting on us; but said he was sick, and had been very little out
for some time. He had guinea-worm, and a cataract was forming in
his eyes. He was dressed in a nearly worn out tobe, and trowsers of
the same colour; and round his head was wrapped an old piece of
yellow coarse cloth for a turban. Notwithstanding the meanness of
the dress, there was something pleasing and prepossessing in his
countenance, and such a freeness as made us soon quite as much
at home as if in our tents. We presented him with a sword, with
which he was highly pleased. Hateeta wished it had been a
bornouse; but we had none with us we considered sufficiently good.
We were led away by the title, sultan. We had no idea the Tuaricks
were so vain; for they used to be filling us with high notions of the
wealth and greatness of the people here.
Our interview was highly interesting; and every one seemed much
pleased with us. The old sultan showed us every kindness; and we
had every reason to believe him sincere in his good wishes. After our
visit we called at the house of Lameens, son of the kadi. He is a
young man of excellent character, and universally respected. His
father is now in Ghadames, arranging with some of the other
principal inhabitants the affairs of the community. He had left
directions with his son to show us every attention. His house was
neatly fitted up, and carpets spread on a high bed, on which we
seated ourselves. Several of the people who were in the castle came
along with us, and by the assistance of those that could speak
Arabic, we were able to keep up a tolerably good conversation. On
inquiring about the Tuarick letters, we found the same sounds given
them as we had before heard from others. We were here at the
fountain head, but were disappointed in not being able to find a book
in the Tuarick language; they told us there was not one. Some only
of the Tuaricks speak the Arabic. We were the more astonished to
find this, when we considered the great intercourse between them
and nations that speak Arabic only.
It was near sunset before we returned to our tents, which were
now tolerably clear of visitors. All had retired to pray and eat. Stewed
meat, bread, and soup, were again sent us by the sultan.
In the evening Hateeta’s kinswomen returned. They were greatly
amused, and laughed heartily at our blundering out a few Tuarick
words. It may be well supposed we were very unfit companions for
the ladies, as they could speak no other language than their own,
and we knew very little of it. Still, however, we got on well, and were
mutually pleased. I could scarcely refrain laughing several times at
the grave manner Clapperton assumed. He had been tutored by
Hateeta, and fully acted up to his instructions; no Tuarick could have
done better. Our friend Hateeta was anxious we should shine, and
read a number of lectures to Clapperton. He is naturally lively, and
full of humour. He was directed not to laugh or sing, but look as
grave as possible, which Hateeta said would be sure to please the
grave Tuaricks. As for myself, I had a natural sedateness, which
Hateeta thought would do. The liveliness of the women, their
freeness with the men, and the marked attention the latter paid them,
formed a striking contrast with other Mahommedan states. Thus the
day passed over well, and we had every reason to be pleased with
the demeanour of the people, and the attention they paid us.
Violent gale from the northward, which almost smothered us with
sand.
About four we went to visit the spring we had heard so much
about, and to make a circuit round the town. The water is contained
in a large reservoir, surrounded with palm trees; and the banks are
covered with rushes, except when the people go to draw water.
There is not that bubbling up we saw in the spring of Shiati and
Ludinat, but apparently an oozing from a large surface, as in those of
Traghan. The water is clear, well tasted, and in abundance: a large
extent of soil is supplied by it, through channels cut in the ground;
and all the town is supplied from this place. Good water and plenty of
it is a great blessing in every quarter of the globe, but much more in
a hot climate. The people are sensible of it; for you hear this place
and that praised for the abundance of this water, and the healthiness
of its quality; and you often hear them say that it does not engender
bile, as the waters of such a place. Some small spots here are really
beautiful, from the diversity of scenery in a small compass. Here and
there patches of grass and beds of water melons, in the edge of the
water channels, fine palms loaded with ripe fruit, small squares of
gufolly and cassoub, and beautiful vines clinging to the trees; in the
brakes, the town and black tinted low hills. We observed platforms,
of palm leaves, raised about five feet from the ground, for the
purpose of sleeping, and defending the person from scorpions,
which are very common.
We now came in sight of the town, and were well pleased with the
appearance. The houses neat and clean; and the mosque, finer than
any thing of the kind in Fezzan. All was neat and simple. It is built at
the foot of a low hill, on the summit of which the former town stood;
but it, as we were told, was destroyed, and the greater part of the
inhabitants, by the giving way of the portion of hill on which it was
erected. The hills, composed as those about here, are very apt to fall
down in large masses. Indeed, none of the hills appear of their
original height. It was not long since a large portion of a neighbouring
mountain gave way, and the noise of its fall was heard at a great
distance. When the melancholy event of the destruction of the town
took place, we could not learn.
The town is surrounded with walls, in good repair, formed of sand
and whitish clay, that gives a clean and lively appearance to the
whole. There is only one gate opening to the east; formerly there
were more, that are now blocked up. The houses are built of the
same substance as the walls, and the external form and internal
arrangement the same as those of Mourzuk and other Mahommedan
towns. The town appears to be about the size of Oubari, and
perhaps contains about 1000 inhabitants. The burying-ground is
outside the town; it is divided into two departments, one for those
arrived at maturity, the other for children,—a distinction not
observable in Fezzan.
In our walk we fell in with a number of females, who had come out
to see us. All were free and lively, and not at all deterred by the
presence of the men. Several had fine features; but only one or two
could be called beautiful. Many of the natives came out of their
houses as we passed along, and cordially welcomed us to their
town. It was done in such a manner, that we could not but feel
pleased and highly flattered.
In the evening we heard a numerous band of females singing at a
distance, which was continued till near midnight. The women were
principally those of the country. This custom is very common among
the people, and is one of the principal amusements in the mountain
recesses. Hateeta said they go out when their work is finished in the
evening, and remain till near midnight in singing and telling stories,
return home, take supper, and go to bed.
The language of the Tuaricks is harsh and guttural; but it has
great strength, and is evidently expressive. That at least is the
opinion we were led to form, with our imperfect knowledge. The want
of books, and the little attention paid to cultivating a language, must
tend to keep it in a very imperfect state. The sedate character of the
Tuarick seems to be a firm barrier against the muses. The males
seldom sing; it is considered an amusement fit only for females.
Their songs were described to us as sweet. We never heard one
repeating any poetical lines. The people have good sound sense,
and give more distinct accounts of what they have seen than I have
received from the Moorish merchants. They would be a shining
people, were they placed in more favourable situations. On almost
every stone in places they frequent, the Tuarick characters are hewn
out. It matters nothing whether the letters are written from the right to
the left, or vice versa, or written horizontally.
LETTERS.
Yet. Yuz.
Yuk. Iz.
Yugh.
Yow Yew.
A. Yid.
Yib. Yir.
Yes. Yei.
Yim. Yigh.
Yish. Yin.
Yill.
IN
AFRICA.
CHAPTER I.
FROM MOURZUK TO KOUKA IN BORNOU.
The Tibboos and Arabs kept us awake half the night with their
singing and dancing, in consequence of the bousafer, or feast, on
entering the Tibboo country. Boo-Khaloom gave two camels, and we
gave one. Our sick seemed to gain a little strength; and as we had
succeeded in purchasing a sheep, a little soup seemed to revive
them much; but we feared that Hillman and one of the servants must
be left behind. However distressing would such an event have been,
it was impossible for men who could not sit upright on a mule to
commence a journey of fifteen days over a desert, during which
travellers are obliged to march from sunrise until dark.
The 12th December was a beautifully mild morning, the
thermometer at eight being at 56. After breakfast, all seemed
revived; but it was with pain I observed the exceeding weakness of
Doctor Oudney and Hillman. I managed to get a sketch of the castle
of Tegerhy from the south side of a salt pool close to the town; the
entrance to which is small, low, and arched, something resembling a
sally-port: within a second wall and gateway, there are loop-holes
which would render the entrance by the narrow passage before
mentioned extremely difficult: above the second gateway, there is
also an opening from whence missiles and firebrands, of which the
Arabs formerly made great use, might be poured on the assailants.
Wells of water are within the walls, and tolerably good; and with
supplies, when in a state of repair, I have no doubt Tegerhy might
make a very good defence.
The sultans of Fezzan probably think that the only means of
keeping these people in order is by keeping them poor. Their only
produce is dates, but those are of excellent quality. No vegetables
are raised here, and we could not even procure an onion. Almost
every town in Africa has its charm or wonder, and Tegerhy is not
without one. There is a well just outside the castle gates, the water of
which, we were told most gravely, “always rose when a kafila was
coming near the town! that the inhabitants always prepared what
they had to sell on seeing this water increase in bulk, for it never
deceived them!”—In proof of this assertion, they pointed out to me
how much higher the water had been previous to our arrival than it
was at the moment we were standing on the brink. This I could have
explained by the number of camels that had drank at it, but I saw it
was better policy to believe what every body allowed to be true: even
Boo-Khaloom exclaimed, “Allah! God is great, powerful, and wise!
How wonderful! Oh!” Over the inner gate of the castle there is a large
hole through to the gateway underneath, and they tell a story of a
woman dropping from thence a stone on the head of some leader
who had gained the outer wall, giving him, by that means, the death
of Abimelech in sacred history.
The situation of Tegerhy is rather pleasing than otherwise: it is
surrounded by date trees, and the water is excellent; a range of low
hills extends to the eastward; and snipes, wild-ducks, and geese,
frequent the salt-pools, which are near the town. The natives are
quite black, but have not the negro face: the men are slim, very
plain, with high cheek bones, the negro nose, large mouth, teeth
much stained by the quantity of tobacco and trona (or muriate of
soda) which they eat; and even snuff, when given to them, goes
directly into their mouths.
The young girls are most of them pretty, but less so than those of
Gatrone. The men always carry two daggers, one about eighteen
inches, and the other six inches, the latter of which is attached to a
ring and worn on the arm or wrist. A Tibboo once told me, pointing to
the long one, “this is my gun; and this,” showing the smaller of the
two, “is my pistol.” The women make baskets and drinking-bowls of
palm leaves with great neatness.
On the 13th, we left Tegerhy, and proceeded on the desert: it was
scattered with mounds of earth and sand, covered with athila (a plant
the camels eat with avidity), and other shrubs. After travelling six
miles we arrived at a well called Omah, where our tents were
pitched, and here we halted three days. On the 16th, after clearing
the palm trees, by which Omah is surrounded, we proceeded on the
desert. About nine we had a slight shower of rain. At three in the
afternoon, we came to a halt at Ghad, after travelling ten miles. Near
the wells of Omah, numbers of human skeletons, or parts of
skeletons, lay scattered on the sands. Hillman, who had suffered
dreadfully since leaving Tegerhy, was greatly shocked at these
whitened skulls, and unhallowed remains; so much so, as to want all
the encouragement I could administer to him.
Dec. 17.—We continued our course over a stony plain, without
the least appearance of vegetation. Coarse opal and sand-stone[10]
strewed the path. We saw Alowere-Seghrir, a ridge of hills, bearing
east by south; Alowere-El-Kebir, a still higher ridge, lies more to the
east, but was not visible. These, by the accounts of the natives, are
the highest mountains in the Tibboo country, with the exception of
Ercherdat Erner. More to the south, the inhabitants are called
Tibboo-Irchad (the Tibboos of the rocks). Through passes in both
these mountains, the road lies to Kanem. About sunset, we halted
near a well, within a half mile of Meshroo. Round this spot were lying
more than one hundred skeletons, some of them with the skin still
remaining attached to the bones—not even a little sand thrown over
them. The Arabs laughed heartily at my expression of horror, and
said, “they were only blacks, nam boo!” (damn their fathers!) and
began knocking about the limbs with the butt end of their firelocks,
saying, “This was a woman! This was a youngster!” and such like
unfeeling expressions. The greater part of the unhappy people, of
whom these were the remains, had formed the spoils of the sultan of
Fezzan the year before. I was assured that they had left Bornou with
not above a quarter’s allowance for each; and that more died from
want than fatigue: they were marched off with chains round their
necks and legs: the most robust only arrived in Fezzan in a very
debilitated state, and were there fattened for the Tripoli slave market.
Our camels did not come up until it was quite dark, and we
bivouacked in the midst of these unearthed remains of the victims of
persecution and avarice, after a long day’s journey of twenty-six
miles, in the course of which, one of our party counted 107 of these
skeletons.
Dec. 19[11].—Moved round a winding pass to the west, and after
an ascent of three hundred feet descended a sandy steep to the
east. This was rather a picturesque spot, looking back upon Thenea.
Our road lay over a long plain with a slight ridge. A fine naga (she-
camel) lay down on the road this day, as I thought from fatigue. The
Arabs crowded round, and commenced unloading her, when, upon
inquiry, I found that she was suddenly taken in labour: about five
minutes completed the operation,—a very fine little animal was
literally dragged into light. It was then thrown across another camel;
and the mother, after being reloaded, followed quietly after her
offspring. One of the skeletons we passed to-day had a very fresh
appearance; the beard was still hanging to the skin of the face, and
the features were still discernible. A merchant, travelling with the
kafila, suddenly exclaimed, “That was my slave! I left him behind four
months ago, near this spot.”—“Make haste! take him to the fsug”
(market), said an Arab wag, “for fear any body else should claim
him.” We had no water, and a most fatiguing day.
Dec. 20 was also a dreary day of most uninteresting country; and
it was 5. 30. when we arrived at the Hormut-el-Wahr. These were the
highest hills we had seen since leaving Fezzan: the highest peak
might be five or six hundred feet. They had a bold black appearance,
and were a relief to the eye, after the long level we had quitted. We
entered the pass, which is nearly two miles in width, and wound
round some high hills to the south: the path was rugged and irregular
in the extreme, and bordered by bold conical and table-topped
detached hills. We blundered and stumbled on until ten at night,
when we found the resting-place, after a toilsome and most
distressing day. We were several times foiled in our attempt to find a
path into the wadey, under these hills, by which the camels might
move, and where the water was. Hillman was exceedingly ill this
night, and Dr. Oudney too fatigued to render him any assistance. El-
Wahr is a wadey of loose gravel, and has a well of good water. This
was the eighth day since our camels had tasted water: they were
weak and sore-footed, from the stony nature of the passes in these
hills of El-Wahr. At night it blew a hurricane.
It is three miles from where we halted to the end of the wadey;
where, to the west, there is a high hill called El-Baab. These hills
extend away to the east, and form part of the range which are found
near Tibesty, where they become higher and bolder. We had now a
stony plain, with low hills of sand and gravel, till we reached El-
Garha, which is a detached conical hill to the west, close to our road;
and here we halted for the night[12].
Dec. 22.—We moved before daylight, passing some rough sand
hills, mixed with red stone, to the west, over a plain of fine gravel,
and halted at the maten, called El-Hammar, close under a bluff head,
which had been in view since quitting our encampment in the
morning. Strict orders had been given this day for the camels to keep
close up, and for the Arabs not to straggle—the Tibboo Arabs having
been seen on the look out. During the last two days, we had passed
on an average from sixty to eighty or ninety skeletons each day; but
the numbers that lay about the wells at El-Hammar were countless:
those of two women, whose perfect and regular teeth bespoke them
young, were particularly shocking; their arms still remained clasped
round each other as they had expired; although the flesh had long
since perished by being exposed to the burning rays of the sun, and
the blackened bones only left: the nails of the fingers, and some of
the sinews of the hand, also remained; and part of the tongue of one
of them still appeared through the teeth. We had now passed six
days of desert without the slightest appearance of vegetation, and a
little branch of the souak was brought me here as a comfort and
curiosity. On the following day we had alternately plains of sand and
loose gravel, and had a distant view of some hills to the west. While I
was dozing on my horse about noon, overcome by the heat of the
sun, which at that time of the day always shone with great power, I
was suddenly awakened by a crashing under his feet, which startled
me excessively. I found that my steed had, without any sensation of
shame or alarm, stepped upon the perfect skeletons of two human
beings, cracking their brittle bones under his feet, and, by one trip of
his foot, separating a skull from the trunk, which rolled on like a ball
before him. This event gave me a sensation which it took some time
to remove. My horse was for many days not looked upon with the
same regard as formerly.
Dec. 24.—When the rains fall, which they do here in torrents in
the season, a sort of grass quickly springs up many feet high. In
passing the desert, a few remaining roots of this dried grass, which
had been blown by the winds from Bodemam, were eagerly seized
on by the Arabs, with cries of joy, for their hungry camels. The plain
was this day covered with slight irregularities, and strewed with
various coloured stones—thick beds of gypsum, stones resembling
topazes, and pieces of calcareous spar, which, reflecting the rays of
the setting-sun, displayed a most beautiful variety of tints. An
incrustation of fine whiting lay in patches, at no great distance from
the well: and soon after the sun had retired behind the hills to the
west, we descended into a wadey, where about a dozen stunted
bushes (not trees) of palm marked the spot where the water was to
be found. Even these miserable bushes were a great relief to the
eye, after the dreary sameness of the preceding days; and at day-
break in the morning, I could not help smiling at seeing Hillman
gazing at them with pleasure in his look, while he declared they
reminded him of a valley near his own home in the West of England.
The wells are situated under a ridge of low white hills of sandstone,
called Mafrasben-Kasarettsa, where there are also beds and hills of
limestone. The wells were so choked up with sand, that several cart-
loads of it were removed previous to finding sufficient water; and
even then the animals could not drink until near ten at night.
One of our nagas had this day her accouchement on the road;
and we all looked forward to the milk which the Arabs assured us
she had in abundance, and envied us not a little our morning
draughts, which we were already quaffing in imagination. However,
one of the “many slips between the cup and the lip” was to befall us.
The poor thing suddenly fell, and as suddenly died: the exclamations
of the Arabs were dreadful.—“The evil eye! the evil eye!” they all
exclaimed—“She was sure to die, I knew it.”—“Well! if she had been
mine, I would rather have lost a child, or three slaves!”—“God be
praised! God is great, powerful, and wise! those looks of the people
are always fatal.”
Dec. 25.—This was a beautiful mild morning: the thermometer 54.
at 6. 30. Our skins were here filled with water which was not
disagreeable, although strongly impregnated with sulphur. The
camels moved at eight. The head of a range of high hills bore west-
south-west, called Tiggerindumma: they resemble in shape and
structure those we had passed nearer: they extend to the west, as
far as the Arooda, five days hence, where there is a well; and ten
days beyond which is Ghraat. At the distance of four miles from
Mafras, we came to a small wadey, where we saw the first dome
date trees: they were full of fruit, though green. We continued
winding amongst a nest of hills,—crossed two water-courses, in
which were tulloh and dry grass (ashub), until seven. These hills are
bold and picturesque, composed of black and coloured sandstone.
No water.
Dec. 26[13].—We emerged from the hills, and broke into a plain,
extending to the east as far as the eye could reach; to the west,
Tiggerindumma sweeps off, and forming nearly a semi-circle,
appears again to the south, a very handsome range, though not
exceeding six hundred feet at any part in height. After passing
between two low ridges of dark hills, we opened on a plain bounded
with flat-topped and conical hills, called La Gaba. We found pieces of
iron ore this day, kidney-shaped, and of various other forms. We
travelled till nine at night, when some of us were nearly falling from
our horses with fatigue. After a narrow stony pass, we came to a halt
in a wadey called Izhya.
Here we had a gale of wind from the north-east for three days.
Our tents were nearly buried in sand, and we were obliged to roll
ourselves up in blankets nearly the whole time.
Dec. 30.—Izhya is called by the Tibboos Yaat. There are here four
wells, which resemble troughs cut in the sand, two or three feet
deep; and it is said, that by thus digging, water may be found in any
part of the wadey. We were encamped nearly west of the wells,
about one hundred yards between them and a raas, or head, which
had been in sight for some time. This head is a land-mark to kafilas,
coming in all directions, who wish to make the wadey. We passed
Ametradumma about four hours; from which, to the north-west, is a
wadey of palms, called Seggedem, with sweet water: here is
generally a tribe of plundering Tibboos, who are always on the look
out for small kafilas. No water.
Dec. 31.—A cold shivering morning. At 7. 30. thermometer 49.,
and we had a long day’s march over a plain, varying but little from
Izhya. The Arabs had no knowledge of the road; and the Tibboo
guide was all we had to rely on. We kept on until late, when the
Tibboo acknowledged he had lost the road, that the well was not far
off, but where he knew not; we therefore halted under some low
brown sandstone hills, and determined on waiting for the daylight.
We lost a camel this day from fatigue.
1823.