Programming and Analytical ToolingIntroductionData science is not only the study of statistical methods and machine-learning algorithms. It is also a practical discipline of constructing reliable computational workflows. A data scientist must acquire data, inspect its structure, transform it, analyse it, communicate findings, and preserve sufficient evidence that another person can reproduce the work. Programming languages, databases, notebooks, development environments, libraries, command-line tools, and version-control systems provide the technical foundation for these activities. Python and R are the two most widely used general-purpose environments in data science. Python is particularly strong in software integration, automation, machine learning, and production engineering. R is especially strong in statistical analysis, data visualisation, and research-oriented workflows. SQL remains essential because much organisational data is stored in relational database systems. These tools are complementary rather than mutually exclusive. This chapter develops a professional workflow rather than teaching isolated commands. The central principle is that analytical code should be readable, testable, traceable, secure, and reproducible. Learning ObjectivesBy the end of this chapter, a learner should be able to: - Explain the roles of Python, R, SQL, notebooks, libraries, and development environments.
- Select suitable tools for different stages of a data-science project.
- Write basic Python and SQL code for data inspection and transformation.
- Distinguish exploratory notebook work from maintainable production code.
- Use environments, package management, command-line tools, and version control responsibly.
- Apply reproducible-coding practices, testing, documentation, and data-governance principles.
- Evaluate a practical data-science workflow and identify its technical risks.
1. The Analytical Computing StackA data-science project normally uses several layers: | Layer | Main purpose | Typical tools |
|---|
| Operating system and shell | Files, processes, automation | Bash, PowerShell, terminal | | Programming language | Logic, transformation, modelling | Python, R | | Database language | Querying structured data | SQL | | Libraries | Reusable specialist functionality | pandas, NumPy, scikit-learn, tidyverse | | Development environment | Editing, running, debugging | VS Code, RStudio, PyCharm | | Notebook interface | Interactive computation and explanation | Jupyter, Quarto, R Markdown | | Version control | History, collaboration, rollback | Git, GitHub or equivalent | | Execution environment | Consistent runtime | virtual environments, conda, containers |
These layers solve different problems. A notebook is convenient for exploration, but it is not a substitute for version control. A library accelerates implementation, but it does not remove the need to understand assumptions. SQL can filter millions of rows close to the data source, whereas Python or R may be more suitable for modelling after a smaller dataset has been extracted. A typical flow is: Data source → SQL extraction → Python/R transformation → analysis/model
→ validation → visualisation/report → versioned outputThe flow is iterative rather than strictly linear. Findings may reveal that the extraction query, data definition, or quality checks must be revised. 2. Python and R as Data-Science LanguagesPythonPython is a general-purpose language with readable syntax and a large ecosystem. Common data-science libraries include: - NumPy for numerical arrays and vectorised computation.
- pandas for tabular data structures, joins, grouping, and reshaping.
- Matplotlib and seaborn for visualisation.
- SciPy for scientific and statistical routines.
- scikit-learn for classical machine learning and model evaluation.
- Jupyter for interactive notebooks.
A pandas DataFrame is a labelled two-dimensional table. For example: import pandas as pd
sales = pd.read_csv("data/sales.csv")
print(sales.shape)
print(sales.head())
print(sales.isna().sum())The code loads a comma-separated file, reports its dimensions, displays initial records, and counts missing values by column. It does not prove that the data is correct; it only begins inspection. A grouped summary can be written as: summary = (
sales.groupby("region", as_index=False)
.agg(total_revenue=("revenue", "sum"),
average_order=("revenue", "mean"),
orders=("order_id", "nunique"))
)The aggregation must be interpreted carefully. If one order appears on several product lines, counting rows would overstate the number of orders; counting unique order identifiers is more appropriate. RR is designed for statistical computing and graphics. Its widely used tidyverse packages include dplyr for data manipulation, ggplot2 for graphics, tidyr for reshaping, and readr for data import. library(dplyr)
sales <- read.csv("data/sales.csv")
summary <- sales |>
group_by(region) |>
summarise(
total_revenue = sum(revenue, na.rm = TRUE),
average_order = mean(revenue, na.rm = TRUE),
orders = n_distinct(order_id)
)The na.rm = TRUE argument changes how missing values are handled. Omitting it may cause a summary to return NA, but automatically excluding missing values may also hide a data-quality problem. The analyst should report the missingness and justify the treatment. Choosing between Python and RTool choice should be based on the task, team capability, existing systems, and deployment requirements. R may be efficient for statistical reporting and exploratory graphics; Python may be preferable when analysis must become part of a web service or automated application. Either language can perform most common analytical tasks. In professional settings, interoperability is often more valuable than ideological preference. 3. SQL and Relational DataSQL operates on relational tables. A table contains rows, representing records, and columns, representing attributes. A primary key uniquely identifies a row. A foreign key refers to a key in another table and supports relationships. A basic query is: SELECT customer_id, order_date, revenue
FROM orders
WHERE order_date >= '2025-01-01'
AND status = 'completed'; SQL clauses are logically evaluated in an order that differs from their written order: rows are selected from the source, filtered by WHERE, grouped when required, filtered by HAVING, projected by SELECT, ordered, and finally limited. Understanding this distinction helps explain why an aggregate alias generally cannot be used in WHERE. A summary query is: SELECT region,
SUM(revenue) AS total_revenue,
COUNT(DISTINCT order_id) AS orders
FROM orders
WHERE status = 'completed'
GROUP BY region
HAVING SUM(revenue) > 100000
ORDER BY total_revenue DESC;COUNT(DISTINCT order_id) is important when the table stores one row per item rather than one row per order.
JoinsSuppose orders contains customer_id and customers contains one row per customer. An inner join returns only matching records: SELECT o.order_id, c.segment, o.revenue
FROM orders AS o
JOIN customers AS c
ON o.customer_id = c.customer_id; A left join retains every row from the left table, even if no match exists on the right. This is useful for finding unmatched records, but it can also duplicate rows if the join key is not unique. Before joining, the analyst should establish the expected relationship: one-to-one, one-to-many, or many-to-many. SQL efficiency and safetyFiltering and selecting only required columns reduces data transfer. Indexes can accelerate searches on frequently filtered or joined columns, although indexes consume storage and can slow writes. Query execution plans should be inspected for large workloads. Never construct SQL by concatenating untrusted user input. Parameterised queries protect against SQL injection: query = "SELECT * FROM orders WHERE customer_id = ?"
result = connection.execute(query, (customer_id,)) The exact placeholder syntax varies by database driver, but the principle is consistent: values should be bound as parameters rather than inserted into query text. 4. Notebooks and Development EnvironmentsA notebook combines executable code, output, explanatory text, and visualisations. It is effective for: - rapidly inspecting unfamiliar data;
- trying alternative transformations;
- communicating an analytical narrative;
- recording intermediate results;
- teaching and presenting methods.
However, notebooks have risks. Cells can be executed out of order, hidden state can remain in memory, outputs can become stale, and large files can produce unwieldy version-control changes. A notebook that runs successfully only after a particular sequence of manual actions is not reproducible. Good notebook practice includes: - Start with a clear purpose and data description.
- Use small, logically ordered sections.
- Restart the kernel and run all cells before sharing.
- Avoid embedding passwords, tokens, or confidential data.
- Set random seeds when stochastic procedures are used, while recognising that exact reproducibility may still depend on software and hardware.
- Move reusable functions into tested modules.
- Record input-data versions and package versions.
- Keep generated outputs separate from source code where practical.
An integrated development environment (IDE) provides editing, code completion, debugging, project navigation, and testing. VS Code and PyCharm are common Python environments; RStudio is widely used for R. The choice matters less than disciplined project structure and reliable execution. A useful project layout is: project/
├── README.md
├── pyproject.toml or requirements.txt
├── data/
│ ├── raw/
│ └── processed/
├── notebooks/
├── src/
├── tests/
├── reports/
└── .gitignore Raw data should normally be preserved as an immutable input. Transformations should produce new files or database tables rather than silently overwriting the original. 5. Libraries, Environments, and Dependency ManagementA library is reusable code distributed for a defined purpose. Libraries increase productivity but introduce dependencies: a project depends on particular packages, versions, operating-system features, and sometimes external services. An environment isolates project dependencies. A Python environment can be created with: python -m venv .venv
source .venv/bin/activate # macOS/Linux
# .venv\Scripts\activate # Windows
python -m pip install pandas
python -m pip freeze > requirements.txt A colleague can install the recorded packages with: python -m pip install -r requirements.txt A requirements file is useful but may not fully capture operating-system libraries or transitive dependency resolution. Modern project configuration tools can declare dependencies and lock exact versions. Conda is also useful when binary scientific dependencies are important. Dependency management should distinguish between: - direct dependencies, explicitly used by the project;
- transitive dependencies, installed because another package requires them;
- development dependencies, such as testing and formatting tools;
- runtime dependencies, needed to execute the application.
Packages should be updated deliberately. Updates may provide security fixes, but they can also change behaviour. A continuous integration system can test updates before deployment. 6. Command-Line Tools and AutomationThe command line is efficient for repeatable operations. Examples include listing files, searching logs, running scripts, and invoking tests: python src/prepare_data.py --input data/raw/sales.csv \
--output data/processed/sales.parquet
python -m pytest tests/Shell scripts can connect steps into pipelines, but they require careful handling of spaces, missing files, exit codes, and sensitive arguments. A robust command-line program should provide a help message, validate inputs, return a meaningful failure status, and write logs. Automation reduces manual variation. A scheduled workflow might download data, validate its schema, run transformations, execute tests, and publish a report. Automation does not guarantee correctness: an automated incorrect query can generate incorrect results more quickly. Validation remains essential. 7. Version Control with GitVersion control records changes to files and supports collaboration. Git works through a local repository and records snapshots called commits. A basic workflow is: git init
git add src/ tests/ README.md
git commit -m "Add initial sales transformation"
git switch -c feature/customer-segmentation A commit should represent a coherent change and have a descriptive message. Branches allow work to be developed separately before review and integration. Pull requests or merge requests provide a place for code review, automated testing, and discussion. Do not commit passwords, private keys, personal records, or very large generated files. A .gitignore file can exclude virtual environments, caches, and local configuration files. Removing a secret from the latest file is not necessarily sufficient because it may remain in repository history; exposed credentials should be revoked and replaced. Version control also applies to analytical definitions. A change from “revenue before tax” to “revenue after discount” can alter a business conclusion even if the code change is small. Definitions, schemas, data dictionaries, and model parameters should therefore be reviewed like code. 8. Reproducible and Reliable CodingReproducibility means that an analysis can be rerun and produce the same or appropriately equivalent result using documented inputs, code, dependencies, and procedures. Computational reproducibility is different from replicability in a new study: the former concerns repeating the same workflow, while the latter concerns obtaining consistent findings independently. A reproducible workflow should specify: - the source and extraction date of data;
- data-cleaning decisions and business definitions;
- software and package versions;
- random seeds where relevant;
- execution instructions;
- expected outputs and validation checks;
- limitations and known exceptions.
TestingTests convert assumptions into executable checks. A unit test checks a small function; an integration test checks interaction among components; a data-quality test checks properties of an input or output dataset. Examples of data-quality assertions include: assert sales["order_id"].notna().all()
assert (sales["revenue"] >= 0).all()
assert sales["order_id"].is_unique The last assertion is valid only if the table is intended to contain one row per order. Tests must reflect the data model, not arbitrary preferences. Useful quality dimensions include completeness, validity, consistency, uniqueness, timeliness, and accuracy. A missing value is not automatically an error: for some variables, absence may be meaningful. The treatment must be documented. Code qualityReadable code uses meaningful names, small functions, explicit assumptions, and consistent formatting. Comments should explain why a non-obvious decision was made, not merely restate the syntax. Linters and formatters reduce avoidable inconsistency. Type hints, where suitable, clarify expected inputs and outputs. A transformation function should ideally be deterministic, meaning that the same valid input and configuration produce the same output. Side effects such as modifying global state or writing untracked files make testing more difficult. 9. A Worked Analytical ExampleAssume a retail table contains one row per order line with fields order_id, region, quantity, unit_price, discount, and status. Let line revenue be defined as: ri = qi pi (1 − di) Here, ri is revenue for line i in currency units, qi is quantity, pi is unit price, and di is the discount proportion. The formula assumes that the discount is expressed as a value between 0 and 1 and that tax, shipping, returns, and other adjustments are excluded. For quantity 3, unit price 50, and discount 0.10: ri = 3 × 50 × (1 − 0.10) = 135 If an order contains several lines, order revenue is the sum of its line revenues: Rj = ∑i ∈ j ri Here, Rj is the revenue of order j, and the summation includes every line belonging to that order. Summing line revenue by region gives regional revenue. The calculation is valid only if line quantities, prices, and discounts use compatible units and if returns are handled consistently. Python implementation: sales = sales.assign(
line_revenue=sales["quantity"] * sales["unit_price"] *
(1 - sales["discount"])
)
completed = sales.query("status == 'completed'")
regional = (completed.groupby("region", as_index=False)
.agg(revenue=("line_revenue", "sum"),
orders=("order_id", "nunique")))Equivalent SQL might calculate the metric close to the database: SELECT region,
SUM(quantity * unit_price * (1 - discount)) AS revenue,
COUNT(DISTINCT order_id) AS orders
FROM order_lines
WHERE status = 'completed'
GROUP BY region;The Python and SQL results should be reconciled on a test sample. If they differ, possible causes include null arithmetic, duplicate rows, differing status definitions, rounding, or inconsistent discount representation. A professional workflow does not assume that two implementations agree merely because their formulas appear similar. 10. Case Study: Reproducible Customer-Complaint AnalysisThis is an illustrative case. A telecommunications company wants to understand whether complaint-resolution time is increasing across service regions. Background and problemThe company stores complaints in a relational database, but analysts previously downloaded spreadsheets manually. Each analyst applied different filters and sometimes counted complaint messages rather than unique complaints. Management received conflicting reports. AnalysisThe team first defined the unit of analysis as one complaint, identified complaint_id as the key, and documented the start and end timestamps. Resolution time was defined as: T = t end − t start where T is elapsed time and both timestamps use the same time zone. Complaints still open at extraction were excluded from completed-resolution analysis but reported separately. A SQL query extracted only required fields and selected one canonical status record per complaint. Python then validated uniqueness, checked that resolution time was non-negative, grouped results by region and month, and produced a report. A schema test failed when a new region code appeared; the pipeline stopped rather than silently classifying it as “other.” Approach and solutionThe team created a Git repository containing SQL scripts, Python modules, tests, a data dictionary, and a README. A virtual environment recorded package versions. A scheduled job executed the pipeline after a database snapshot was created. The report included the extraction timestamp, excluded records, data-quality failures, and definitions. OutcomeThe apparent increase in resolution time disappeared after duplicate complaint messages were removed. A genuine delay remained in one region, where staffing records showed a temporary shortage. The organisation therefore avoided an incorrect company-wide intervention and targeted operational support more precisely. Learning pointsThe case illustrates that tooling is inseparable from analytical validity. SQL reduced unnecessary data movement; Python supported validation and reporting; Git preserved the history of definitions; tests exposed schema changes; and documentation made the result auditable. The principal improvement was not a sophisticated algorithm but a controlled, reproducible workflow. 11. Ethics, Security, and GovernanceAnalytical tooling can expose personal, confidential, or commercially sensitive information. Analysts should apply data minimisation: collect and process only what is necessary. Access should follow least privilege, and sensitive data should be protected in transit and at rest. Identifiers should be removed, masked, or pseudonymised when they are not required. A notebook containing a customer identifier, an exported database extract, or a Git commit containing an access token may create a serious incident. Secrets belong in an approved secret-management system, not in source files or notebooks. Governance also concerns interpretation. A reproducible analysis can still be unfair if the data reflects historical discrimination or if a proxy variable produces unequal effects. Analysts should document the population represented, exclusions, measurement limitations, and intended use. Where automated decisions affect people, human oversight, appeal mechanisms, and applicable privacy and anti-discrimination requirements must be considered. Generative AI assistants can help draft code, explain errors, or suggest tests, but generated code may contain security flaws, licensing issues, or incorrect assumptions. Sensitive data should not be pasted into unapproved systems. Human review, testing, and source verification remain necessary. 12. Contemporary Developments and Best PracticesModern data work increasingly uses cloud warehouses, containerised execution, workflow orchestration, data contracts, and continuous integration. These developments improve scalability and reliability but add operational complexity. A small project does not need a large platform; architecture should be proportionate to risk, volume, frequency, and team capability. Recommended practices are: - Define the unit of analysis and business terms before coding.
- Keep raw inputs immutable and record their provenance.
- Push suitable filtering and aggregation to the database.
- Use notebooks for exploration and modules or scripts for reusable logic.
- Isolate and record dependencies.
- Write tests for transformations and data-quality assumptions.
- Use version control for code, definitions, and documentation.
- Review joins for unexpected row multiplication.
- Validate outputs against independent totals or samples.
- Protect credentials and confidential information.
- Automate repeatable execution, but retain monitoring and review.
- Communicate uncertainty, exclusions, and limitations with every important result.
Chapter SummaryProgramming and analytical tooling form an integrated foundation for data science. Python and R provide complementary environments for transformation, statistics, visualisation, and modelling, while SQL enables efficient and controlled access to relational data. Notebooks support exploration and communication, but reusable logic should be moved into maintainable, tested code. Libraries accelerate work but require dependency and environment management. Command-line tools enable automation, and Git preserves the history of code, definitions, and collaboration. Reliable data science requires more than code that runs. It requires explicit data definitions, validation, testing, reproducible environments, secure handling of information, and documentation of assumptions. A professional workflow connects extraction, transformation, analysis, reporting, and governance into an auditable process. Key TermsAPI: An interface through which software systems exchange data or functionality. Branch: An independent line of development in a version-control repository. DataFrame: A labelled, two-dimensional tabular data structure commonly used in Python and R. Data provenance: Information about the origin, transformations, and history of data. Data quality: The degree to which data is complete, valid, consistent, unique, timely, and fit for purpose. Dependency: A software package or system component required by a project. Environment: An isolated collection of software, packages, and configuration used to run a project. Foreign key: A column whose values refer to a key in another relational table. Git: A distributed version-control system. IDE: An integrated development environment for editing, running, debugging, and managing code. Join: An operation that combines rows from tables using related fields. Library: Reusable software providing functions or components for a particular purpose. Notebook: An interactive document combining code, outputs, visualisations, and explanatory text. Parameterised query: A database query in which input values are bound separately from query text. Primary key: A field or combination of fields that uniquely identifies a table row. Reproducibility: The ability to rerun a documented computational workflow and obtain the same or appropriately equivalent result. SQL: Structured Query Language used to manage and query relational data. Unit test: A test of a small, isolated piece of code. Version control: A system for recording file changes and supporting collaboration and rollback. Review QuestionsShort Answer Questions- What are the principal differences between the roles of SQL and Python in a data-science workflow?
- Why can a notebook produce a result that is difficult to reproduce?
- What is the purpose of a virtual environment?
- Explain the difference between a primary key and a foreign key.
- Why should raw data normally be preserved as an immutable input?
Long Answer Questions- Compare Python and R for statistical analysis, visualisation, automation, and production deployment.
- Explain how SQL joins can create unintended duplicate records and describe methods for detecting the problem.
- Design a suitable project structure for a data-science project and justify the role of each major directory.
- Discuss how Git, dependency management, testing, and documentation work together to support reproducibility.
- Evaluate the ethical and security risks associated with notebooks, repositories, and analytical databases.
Analytical / Application Questions- A table contains one row per order line, but an analyst uses
COUNT(*) to report the number of orders. Explain the error and propose a corrected calculation. - Two implementations of regional revenue—one in SQL and one in Python—produce different results. Develop a systematic diagnostic procedure.
- A scheduled pipeline fails because a new category appears in a source column. Should the pipeline automatically classify the value as “other”? Evaluate the alternatives and recommend a governance response.
Multiple Choice Questions with AnswersQuestion 1Which tool is primarily designed for querying and aggregating data stored in relational database tables? A. SQL B. Git C. Jupyter D. Matplotlib Correct Answer: A. SQL Question 2Why is COUNT(DISTINCT order_id) often preferable to COUNT(*) in an order-line table? A. It automatically removes missing prices B. It counts unique orders rather than individual line records C. It converts prices into a common currency D. It sorts orders by date Correct Answer: B. It counts unique orders rather than individual line records Question 3What is the main purpose of a virtual environment? A. To encrypt every database record B. To replace version control C. To isolate project software dependencies D. To generate visualisations automatically Correct Answer: C. To isolate project software dependencies Question 4Which practice best reduces the risk of SQL injection? A. Using longer table names B. Concatenating user input into SQL strings C. Storing queries only in notebooks D. Using parameterised queries Correct Answer: D. Using parameterised queries Question 5What is a major reproducibility risk specific to interactive notebooks? A. They cannot display text B. Cells may be executed out of order, leaving hidden state C. They cannot use libraries D. They always delete raw data Correct Answer: B. Cells may be executed out of order, leaving hidden state Question 6In a relational database, what does a primary key do? A. Uniquely identifies a row B. Stores all foreign tables C. Formats a numerical column D. Automatically creates a visualisation Correct Answer: A. Uniquely identifies a row Question 7For the revenue formula ri = qi pi (1 − di), what does di represent? A. The quantity sold B. The unit price C. The discount proportion D. The order identifier Correct Answer: C. The discount proportion Question 8Which item should generally not be committed to a Git repository? A. A README file B. A unit test C. A data dictionary D. An unrevoked access token Correct Answer: D. An unrevoked access token Question 9What is the most appropriate purpose of a data-quality assertion? A. To make code longer B. To test whether an explicit data assumption holds C. To guarantee that data is unbiased D. To replace business definitions Correct Answer: B. To test whether an explicit data assumption holds Question 10Which approach best distinguishes exploratory work from maintainable analytical software? A. Keep all logic in manually ordered notebook cells B. Avoid recording package versions C. Move reusable logic into tested modules or scripts D. Delete intermediate validation results Correct Answer: C. Move reusable logic into tested modules or scripts
|