business

Data Analytics

PRICE ON REQUEST

Contact me to help with

data driven decisions

Prices depend on the data set size, your goals, budget, timeframe…..

AHK Data Analytics 2005-2024

Questions to AHK

YEAR IN BRIEF

Why do income and costs mirror one another

INCOME

Why do both, ‘Income from damages to property of the Authority’ and ‘Net income from maintenance of public lighting and sale of materials’ end in 2013?

What is ‘interest income: other’?

What is ‘finance income’ and did it end in 2022?

Why does ‘interest income from bank balances’ follow almost exactly the ‘finance income’?

What is included in ‘Sundry Income’?

What is ‘Electricity market income’?

why is desalination power accounted for separately from standard commercial electricity sales?

COSTS

Why do costs follow so closely with income?

Why does income from the sale of electricity almost match the total costs?

EMPLOYEES

Why the steady decline in numbers of employees?

Greenhouse Gas Emission Allowances Cost

Is this cost included in the calculation of electricity tariffs for customers?

Why is AHK reluctant to purchase more electricity from third party suppliers?

Why in the last five years at least, has AHK not done more to reduce this cost by reducing oil consumption and investing in more renewable energy?

KODAP/COSMOS

Why is it both income and cost or neither from 2011 to 2017?

Will KODAP cease after stopping oil powered electricity generation?

Cyprus Transmission System Operator

Why is it both income and cost?

Generation

Why the decrease in generation in 1994 to 1995?

AHK INVESTMENTS

Why is AHK investing in the financial markets in Equity securities and mutual funds and why €226 million?

Subsidiaries & Joint Ventures

What is the reason for continuing with the three dormant subsidiaries?

Are annual accounts available for  AIK-IAK AHERA Ananeosimes?

Why did AHK transfer 50.1% of its ownership to the Holy Archbishopric of Cyprus?

Why the continued investment in the loss making Natural Gas Infrastructure Company, ETYFA deal?

Leases & Assets

Is AHK interested in doing a data analysis on the leases and assets to make data driven decisions?

Tseri Photovoltaic Park

Did AHK buy this land? Is it part of the ‘innovative solutions for land acquisition’ leases and partnerships?

Electricity Storage

Is there any update on the GROW-DERS “Grid reliability and operability with distributed generation using transportable storage” project?

What electricity storage options have been researched?

Is unbundling preventing storage investment?

Board of Directors

In conjunction with the AHK INVESTMENTS questions, why is a religious institution an active investment partner in national electricity generation?

Methodology & Data Sources

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.

I used a function script in google sheets to extract the data from the pdf document This only cleaned up the number format but missed many tables.
				
					function batchExtractPdfsFromFolder() {
  const folderId = "PASTE_YOUR_FOLDER_ID_HERE"; // Put your ID here
  const folder = DriveApp.getFolderById(folderId);
  const files = folder.getFilesByType(MimeType.PDF);
  const ss = SpreadsheetApp.getActiveSpreadsheet();
 
  while (files.hasNext()) {
    const file = files.next();
    Logger.log("Processing: " + file.getName());
 
    // 1. Create a new sheet tab for this file
    // We limit the name length to 30 chars (Sheets limit)
    let sheetName = file.getName().substring(0, 30);
    let sheet = ss.getSheetByName(sheetName) || ss.insertSheet(sheetName);
    sheet.clear(); // Clear existing data if sheet exists
 
    // 2. OCR Conversion (PDF to Temp Doc)
    const resource = {
      title: 'Temp_OCR_' + file.getName(),
      mimeType: file.getMimeType()
    };
    
    // Uses the Advanced Drive Service (ensure it is enabled in 'Services')
    const tempDocFile = Drive.Files.insert(resource, file.getBlob(), {ocr: true});
    
    // 3. Extract text from the temporary Doc
    const doc = DocumentApp.openById(tempDocFile.id);
    const textContent = doc.getBody().getText();
    
    // 4. Split text into rows and write to the sheet
    const rows = textContent.split('\n').map(row => [row]);
    if (rows.length > 0) {
      sheet.getRange(1, 1, rows.length, 1).setValues(rows);
    }
 
    // 5. Cleanup
    Drive.Files.remove(tempDocFile.id);
  }
  
  Logger.log("All files processed!");
}"
				
			
Why “Year in Brief” (p13/15) fails completely: Those early pages (roughly pp1–27 across all reports) were created in FreeHand MX and the text was converted to outlines/paths — meaning the letters are stored as vector shapes, not as text characters. There are literally zero text characters in the PDF’s data layer. No tool (pdfplumber, pdftotext, Tabula, Adobe) can extract text from those pages because there is no text to extract. The only way to read them is visually, by rasterising the page and reading it as an image.
				
					function batchExtractPdfsFromFolder() {
  const folderId = "-------------------"; // Put your ID here
  const folder = DriveApp.getFolderById(folderId);
  const files = folder.getFilesByType(MimeType.PDF);
  const ss = SpreadsheetApp.getActiveSpreadsheet();


  while (files.hasNext()) {
    const file = files.next();
    Logger.log("Processing: " + file.getName());


    // 1. Create a new sheet tab for this file
    // We limit the name length to 30 chars (Sheets limit)
    let sheetName = file.getName().substring(0, 30);
    let sheet = ss.getSheetByName(sheetName) || ss.insertSheet(sheetName);
    sheet.clear(); // Clear existing data if sheet exists


    // 2. OCR Conversion (PDF to Temp Doc)
    const resource = {
      title: 'Temp_OCR_' + file.getName(),
      mimeType: file.getMimeType()
    };
    
    // Uses the Advanced Drive Service (ensure it is enabled in 'Services')
    const tempDocFile = Drive.Files.insert(resource, file.getBlob(), {ocr: true});
    
    // 3. Extract text from the temporary Doc
    const doc = DocumentApp.openById(tempDocFile.id);
    const textContent = doc.getBody().getText();
    
    // 4. Split text into rows and write to the sheet
    const rows = textContent.split('\n').map(row => [row]);
    if (rows.length > 0) {
      sheet.getRange(1, 1, rows.length, 1).setValues(rows);
    }


    // 5. Cleanup
    Drive.Files.remove(tempDocFile.id);
  }
  
  Logger.log("All files processed!");
}
___________________


function tidyAllAHKSheets() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheets = ss.getSheets();
  
  sheets.forEach(sheet => {
    const sheetName = sheet.getName();
    // Only process the raw year tabs (4 digits)
    if (sheetName.match(/^\d{4}$/)) {
      const targetName = sheetName + "b";
      let targetSheet = ss.getSheetByName(targetName);
      if (!targetSheet) {
        targetSheet = ss.insertSheet(targetName);
      }
      
      const range = sheet.getDataRange();
      const data = range.getValues();
      
      const cleanedData = data.map(row => {
        return row.map(cell => {
          // Convert everything to string for processing, then try to parse
          let val = String(cell).trim();
          if (val === "" || val === "-" || val === "null") return "";
          
          let isNegative = false;
          
          // Check for brackets: (1.000,00)
          if (val.indexOf('(') === 0 && val.indexOf(')') === (val.length - 1)) {
            isNegative = true;
            val = val.replace(/[\(\)]/g, '');
          } 
          // Check for standard minus sign
          else if (val.indexOf('-') === 0) {
            isNegative = true;
            val = val.substring(1);
          }
          
          // Tidy the number: Remove Greek thousands (.) and replace decimal (,) with (.)
          // We use a regex to remove all dots and then switch the comma.
          let numericCandidate = val.replace(/\./g, '').replace(',', '.');
          
          // Try to convert to a number
          let num = parseFloat(numericCandidate);
          
          // If it's a valid number and NOT just a year (like 2024), return formatted number
          // We check if it's a number and if the original string contained a comma or dot 
          // to avoid accidentally turning "Page 1" into "1"
          if (!isNaN(num) && /[\d,.]+/.test(val)) {
             return isNegative ? -num : num;
          }
          
          return cell; // Return original if it's text
        });
      });
      
      targetSheet.clear();
      targetSheet.getRange(1, 1, cleanedData.length, cleanedData[0].length).setValues(cleanedData);
      
      // Formatting the result
      const lastCol = targetSheet.getLastColumn();
      const lastRow = targetSheet.getLastRow();
      if (lastCol > 1) {
        targetSheet.getRange(1, 2, lastRow, lastCol - 1).setNumberFormat("#,##0.00");
      }
    }
  });
}
				
			

Gantt chart challenges

Various methods of visually presenting 65 members of the board of directors of AHK over a period of 20 years was tried.

Modified bar graphs in sheets and excel were tight and restrictive.

Gantt charts were to high to fit on a slide.

This chart uses https://developers.google.com/chart

The data table is the directors name, start date, end date and a calculation of days between the two. The data is grouped by each director so that their tenure appears on one line. You can hover over each tenure period to see the months and days.

Graphics & Video challenges

I created some graphics using vectors in Inkscape and animated them using svgator. The resulting animation can be exported as high-resolution Lotties and animated svgs and lower resolution video formats.

Google Slides does not support lotties or animated vectors despite it being an web based presentation app.

Excel likewise does not support lotties or animated vectors so videos shown are mp4 video exports.

This graphic shows a small file size high resolution scalable animation. 

Donate & Support