Measure Transition Risk for Loan Portfolios Based on Climate Scenarios with Paths
This example shows the effect of transition risk on portfolios of loans from two banks under two different climate scenarios. In this example, each climate scenario uses 400 simulations and the visualizations reflect a distribution of portfolio outcomes.
Download and Import Climate Data
This example uses climate scenario data from the MIT Joint Program on the Science and Policy of Global Change. For details about this freely available data, see the MIT data repository.
Download the raw data from the MathWorks support files area.
filename = matlab.internal.examples.downloadSupportFile("risk","MITUncertaintyData.zip");
Make a folder in your current directory for the climate data.
if not(isfolder("MITUncertaintyData")) mkdir("MITUncertaintyData") end
Unzip the downloaded files into the created folder.
MITDataFolder = fullfile(pwd,"MITUncertaintyData");
unzip(filename,MITDataFolder)
The data set has three climate scenarios. The Scenario_Descriptions.docx
file provides a description of the scenarios.
scenarios = ["1.5C","2C","Ref"];
Read the climate data into MATLAB® by using the helper function importClimateData
in a for
–loop.
energies = ["Coal" "Gas" "Oil" "Nuclear" "Hydro" "Biomass" "Renewables"]; regions = ["GLB" "USA" "CAN" "MEX" "JPN" "ANZ" "EUR" "ROE" "RUS" "ASI" "CHN" "IND" "BRA" "AFR" "MES" "LAM" "REA" "KOR" "IDZ"]; climateDataDictionary = dictionary; for scenario = scenarios for energy = energies for region = regions key = strcat(scenario, energy, region); climateDataDictionary{key} = importClimateData(scenario, energy, region); end end end
Stack the scenarios into a tall data set.
vals = values(climateDataDictionary); allClimateData = vertcat(vals{:}); head(allClimateData)
Simulation Year Energy Use Scenario Region Energy __________ ____ __________ ________ ______ ______ 1 2020 151.31 1.5C GLB Coal 1 2025 137.82 1.5C GLB Coal 1 2030 135.75 1.5C GLB Coal 1 2035 14.962 1.5C GLB Coal 1 2040 18.553 1.5C GLB Coal 1 2045 33.185 1.5C GLB Coal 1 2050 42.375 1.5C GLB Coal 1 2055 40.205 1.5C GLB Coal
Process Climate Data
The tall data set, allClimateData,
has one column for energy use. Unstack the data so that the energy use for each climate scenario has a separate column.
regionEnergyClimateData = unstack(allClimateData, "Energy Use", "Scenario", "GroupingVariables", ["Simulation", "Year", "Region", "Energy"], "NewDataVariableNames", ["EnergyUse1_5C", "EnergyUse2C", "EnergyUseRef"]); regionEnergyClimateData = regionEnergyClimateData(:, ["Region", "Energy", "Year", "Simulation", "EnergyUseRef", "EnergyUse1_5C", "EnergyUse2C"]); head(regionEnergyClimateData)
Region Energy Year Simulation EnergyUseRef EnergyUse1_5C EnergyUse2C ______ ______ ____ __________ ____________ _____________ ___________ GLB Coal 2020 1 156.3 151.31 151.31 GLB Coal 2025 1 158.09 137.82 137.82 GLB Coal 2030 1 159.73 135.75 135.75 GLB Coal 2035 1 167.23 14.962 97.463 GLB Coal 2040 1 158.86 18.553 70.469 GLB Coal 2045 1 159.5 33.185 45.762 GLB Coal 2050 1 161.88 42.375 56.019 GLB Coal 2055 1 162.6 40.205 70.153
Create rows for the total by simulation, year, and region for each scenario. You need the totals to compute market share and shocks.
totalData = groupsummary(regionEnergyClimateData, ... ["Simulation", "Year", "Region"], ... "sum",["EnergyUse1_5C", "EnergyUse2C", "EnergyUseRef"]); totalData = removevars(totalData,"GroupCount"); totalData = renamevars(totalData, ... ["sum_EnergyUse1_5C", "sum_EnergyUse2C", "sum_EnergyUseRef"], ... ["EnergyUse1_5C", "EnergyUse2C", "EnergyUseRef"]); totalData.Energy = repmat("Total",[height(totalData) 1]); totalData.Energy = categorical(totalData.Energy); regionEnergyClimateData = [regionEnergyClimateData; totalData]; regionEnergyClimateData = movevars(regionEnergyClimateData, ... ["Region", "Energy"],"Before","Simulation"); regionEnergyClimateData = sortrows(regionEnergyClimateData, ... ["Region","Energy","Simulation","Year"]);
Join the total data as a column.
totalDataJoin = removevars(totalData,"Energy"); totalDataJoin = renamevars(totalDataJoin,["EnergyUse1_5C" "EnergyUse2C" "EnergyUseRef"], ... ["TotalEnergyUse1_5C" "TotalEnergyUse2C" "TotalEnergyUseRef"]); regionEnergyClimateData = join(regionEnergyClimateData,totalDataJoin);
Define the market share shock. For more details on this definition, see Profitability and Shocks on Market Share.
Let denote the market share at time of sector in the region in scenario . Define similarly. Then, the market share shock is defined as
Compute the market share and shocks.
% Compute in chunks c = cell(1,400); for i = 1:400 dataSlice = regionEnergyClimateData(regionEnergyClimateData.Simulation == i,:); % Market share is forced to be nonzero. dataSlice.MarketShareRef = max(dataSlice.EnergyUseRef ./ dataSlice.TotalEnergyUseRef, 1e-6); dataSlice.MarketShare1_5C = max(dataSlice.EnergyUse1_5C ./ dataSlice.TotalEnergyUse1_5C, 1e-6); dataSlice.MarketShare2C = max(dataSlice.EnergyUse2C ./ dataSlice.TotalEnergyUse2C, 1e-6); % Compute the scenario shocks. dataSlice.Shock1_5C = (dataSlice.MarketShare1_5C - dataSlice.MarketShareRef) ./ dataSlice.MarketShareRef; dataSlice.Shock2C = (dataSlice.MarketShare2C - dataSlice.MarketShareRef) ./ dataSlice.MarketShareRef; % Cap the shock so the maximum positive shock has the same magnitude as % the maximum negative shock dataSlice.Shock1_5CCap = min(dataSlice.Shock1_5C, 1); dataSlice.Shock2CCap = min(dataSlice.Shock2C, 1); c{1,i} = dataSlice; end regionEnergyClimateData = vertcat(c{:}); head(regionEnergyClimateData)
Year Region Energy Simulation EnergyUseRef EnergyUse1_5C EnergyUse2C TotalEnergyUse1_5C TotalEnergyUse2C TotalEnergyUseRef MarketShareRef MarketShare1_5C MarketShare2C Shock1_5C Shock2C Shock1_5CCap Shock2CCap ____ ______ ______ __________ ____________ _____________ ___________ __________________ ________________ _________________ ______________ _______________ _____________ __________ __________ ____________ __________ 2020 GLB Coal 1 156.3 151.31 151.31 562.85 562.85 577.26 0.27077 0.26882 0.26882 -0.0071761 -0.0071761 -0.0071761 -0.0071761 2025 GLB Coal 1 158.09 137.82 137.82 571.77 571.77 613.93 0.25751 0.24104 0.24104 -0.063951 -0.063951 -0.063951 -0.063951 2030 GLB Coal 1 159.73 135.75 135.75 584.18 584.18 636.23 0.25106 0.23237 0.23237 -0.074431 -0.074431 -0.074431 -0.074431 2035 GLB Coal 1 167.23 14.962 97.463 292.45 546.08 690.27 0.24227 0.05116 0.17848 -0.78883 -0.2633 -0.78883 -0.2633 2040 GLB Coal 1 158.86 18.553 70.469 285.89 521.59 719.77 0.22071 0.064898 0.1351 -0.70596 -0.38786 -0.70596 -0.38786 2045 GLB Coal 1 159.5 33.185 45.762 287.79 500.71 753.71 0.21162 0.11531 0.091395 -0.45511 -0.56811 -0.45511 -0.56811 2050 GLB Coal 1 161.88 42.375 56.019 297.72 455.21 788.06 0.20542 0.14233 0.12306 -0.30711 -0.40091 -0.30711 -0.40091 2055 GLB Coal 1 162.6 40.205 70.153 298.28 423.58 813.78 0.19981 0.13479 0.16562 -0.32543 -0.17114 -0.32543 -0.17114
Process Portfolio Data
To process the portfolio data, you use a simulation that is similar to the one in Measure Transition Risk for Loan Portfolios with Respect to Climate Scenarios, but this simulation has more granular regions and additional simulated loan-level data.
First, load the data.
load("ClimateLoanPortfolioDataGranular.mat") ClimateLoanPortfolioDataGranular.BorrowerSector = renamecats(ClimateLoanPortfolioDataGranular.BorrowerSector,["Bioenergy", "Renewables (wind&solar)"],["Biomass","Renewables"]);
Generate histograms to show the distribution of loans by region and sector for the two simulated banks.
LoanPortfolioDataBank1 = ClimateLoanPortfolioDataGranular(ClimateLoanPortfolioDataGranular.Bank=="Bank1",:); LoanPortfolioDataBank2 = ClimateLoanPortfolioDataGranular(ClimateLoanPortfolioDataGranular.Bank=="Bank2",:); [CountsRegion1, BinsRegion1] = histcounts(LoanPortfolioDataBank1.GranularBorrowerRegion); [CountsSector1, BinsSector1] = histcounts(LoanPortfolioDataBank1.BorrowerSector); [CountsRegion2, BinsRegion2] = histcounts(LoanPortfolioDataBank2.GranularBorrowerRegion); [CountsSector2, BinsSector2] = histcounts(LoanPortfolioDataBank2.BorrowerSector); figure tiledlayout(2,1) nexttile b1 = barh(categorical(BinsRegion1), [CountsRegion1; CountsRegion2], "FaceColor", "flat"); xlabel("Loans") ylabel("Regions") title("Distribution of Regions") legend(["Bank 1", "Bank 2"],"Location","best") b1(1).CData = [0.633, 0.078, 0.243]; b1(2).CData = [0.467, 0.675, 0.188]; fig = gcf; currentPosition = get(fig, 'Position'); newHeight = currentPosition(4) * 1.5; set(fig, 'Position', [currentPosition(1), currentPosition(2), currentPosition(3), newHeight]); nexttile b2 = barh(categorical(BinsSector1), [CountsSector1; CountsSector2], "FaceColor", "flat"); xlabel("Loans") ylabel("Sectors") title("Distribution of Sectors") legend(["Bank 1", "Bank 2"],"Location","best") b2(1).CData = [0.633, 0.078, 0.243]; b2(2).CData = [0.467, 0.675, 0.188];
Bank 1 is heavily invested in oil fossil fuel projects, whereas Bank 2 is heavily invested in hydro green energy products. Regarding the geographical distribution of projects, both banks are similar, with a larger number of projects in developing regions like China, India, and Africa. Because Bank 2 requires a faster transition away from fossil fuels, it is likely to outperform Bank 1 more quickly in the 1.5C scenario than in the 2C scenario.
The portfolio represents a snapshot taken on December 31, 2020. This implies a static balance sheet during the time horizon, which might not be realistic. You can use a dynamic balance sheet in this step to analyze different portfolio evolution scenarios. Create a data set that represents the same portfolio with snapshots at five-year intervals from 2020 to 2095.
portfolioSnapshots = dictionary; for year = 2020:5:2095 value = ClimateLoanPortfolioDataGranular; value.Snapshot = categorical(repmat(year,height(ClimateLoanPortfolioDataGranular),1)); portfolioSnapshots{year} = value; end portfolioSnapshots = vertcat(portfolioSnapshots.values{:});
Compute Change in Valuation
Create a large data set that contains each loan and snapshot with the 400 shocks by performing an outer join between the portfolio snapshots and climate data.
First, prepare the climate data for joining, then join the data sets.
regionEnergyClimateDataJoin = table(regionEnergyClimateData.Energy, ... regionEnergyClimateData.Region, ... regionEnergyClimateData.Year, ... regionEnergyClimateData.Simulation, ... regionEnergyClimateData.Shock1_5CCap, ... regionEnergyClimateData.Shock2CCap, ... VariableNames = ... ["BorrowerSector", ... "GranularBorrowerRegion", ... "Snapshot", ... "Simulation", ... "Shock1_5CCap", ... "Shock2CCap"]); portfolioSnapshotsClimateData = outerjoin(portfolioSnapshots,regionEnergyClimateDataJoin,"MergeKeys",true,"Type","left");
Calculate the loan level changes in the probability of default (PD) and valuation by using the following equations derived in Borrower's Default Conditions and Climate Policy Shocks
.
Assume that , , and that controls the size of the exogenous shock . Because the exogenous shock is independent of climate shock, you can chose it independently of the simulation. It suffices to let be the maximum over each loan ID and snapshot pair of the quantity derived in Profitability and Shocks on Market Share.
Next, compute the observation level .
portfolioSnapshotsClimateData.deltajTemp1_5C = 2 .* portfolioSnapshotsClimateData.BookValue .* (1 + abs(portfolioSnapshotsClimateData.Shock1_5CCap)); portfolioSnapshotsClimateData.deltajTemp2C = 2 .* portfolioSnapshotsClimateData.BookValue .* (1 + abs(portfolioSnapshotsClimateData.Shock2CCap));
Find the maximum over each loan ID and snapshot pair, which is independent of the scenario and simulation.
deltaTableTemp = grpstats(portfolioSnapshotsClimateData, ... ["LoanID", "Snapshot"], "max", ... "DataVars", ["deltajTemp1_5C", "deltajTemp2C"]); deltaTableJoin = table; deltaTableJoin.LoanID = deltaTableTemp.LoanID; deltaTableJoin.Snapshot = deltaTableTemp.Snapshot; deltaTableJoin.deltaj1_5C = deltaTableTemp.max_deltajTemp1_5C; deltaTableJoin.deltaj2C = deltaTableTemp.max_deltajTemp2C;
Join the values back to the main data set.
portfolioSnapshotsClimateData = outerjoin(portfolioSnapshotsClimateData, ... deltaTableJoin, "MergeKeys", true, "Type", "left"); portfolioSnapshotsClimateData.pdChange1_5C = -portfolioSnapshotsClimateData.Shock1_5CCap .* portfolioSnapshotsClimateData.BookValue ./ portfolioSnapshotsClimateData.deltajTemp1_5C; portfolioSnapshotsClimateData.pdChange2C = -portfolioSnapshotsClimateData.Shock2CCap .* portfolioSnapshotsClimateData.BookValue ./ portfolioSnapshotsClimateData.deltajTemp2C; portfolioSnapshotsClimateData.valChange1_5C = -portfolioSnapshotsClimateData.FaceValue .* portfolioSnapshotsClimateData.pdChange1_5C; portfolioSnapshotsClimateData.valChange2C = -portfolioSnapshotsClimateData.FaceValue .* portfolioSnapshotsClimateData.pdChange2C;
Aggregate the valuation changes by bank, snapshot, and simulation.
valChangeSummary = groupsummary(portfolioSnapshotsClimateData, ... ["Bank","Snapshot","Simulation"], "sum", ... ["FaceValue","valChange1_5C","valChange2C"]); valChangeSummary.percentChangeVal1_5C = 100 * valChangeSummary.sum_valChange1_5C ./ valChangeSummary.sum_FaceValue; valChangeSummary.percentChangeVal2C = 100 * valChangeSummary.sum_valChange2C ./ valChangeSummary.sum_FaceValue; valChangeSummary = removevars(valChangeSummary, ... ["GroupCount","sum_FaceValue","sum_valChange1_5C","sum_valChange2C"]);
Calculate percentiles for the valuation changes.
valChangePrctiles = groupsummary(valChangeSummary, ... ["Bank","Snapshot"], ... {@(y) prctile(y,5), @(y) prctile(y,50), @(y) prctile(y,95)}); % Rename the percentile variables valChangePrctiles = renamevars(valChangePrctiles, ... ["fun1_percentChangeVal1_5C", ... "fun2_percentChangeVal1_5C", ... "fun3_percentChangeVal1_5C", ... "fun1_percentChangeVal2C", ... "fun2_percentChangeVal2C", ... "fun3_percentChangeVal2C"], ... ["percentChangeVal1_5C_5", ... "percentChangeVal1_5C_50", ... "percentChangeVal1_5C_95", ... "percentChangeVal2C_5", ... "percentChangeVal2C_50", ... "percentChangeVal2C_95"]); valChangePrctiles = removevars(valChangePrctiles, ["fun1_Simulation", "fun2_Simulation", "fun3_Simulation"]);
Visualize Results
Create a histogram of the distribution of changes in valuation by year. In the 1.5C scenario, the greener Bank 2 distribution separates from Bank 1 much faster than in the 2C scenario.
scenario = "1.5C"; scenarioDataName = matlab.lang.makeValidName("percentChangeVal" + scenario); figure; t = tiledlayout(5,3); for year = 2025:5:2095 bank1Data = valChangeSummary(valChangeSummary.Snapshot == string(year) & valChangeSummary.Bank == "Bank1",:); bank2Data = valChangeSummary(valChangeSummary.Snapshot == string(year) & valChangeSummary.Bank == "Bank2",:); ax = nexttile; h1 = histogram(ax, bank1Data.(scenarioDataName)); hold(ax, "on"); h2 = histogram(ax, bank2Data.(scenarioDataName)); h1.BinWidth = .5; h2.BinWidth = .5; titleString = string(year); title(ax, titleString); xlim(ax, [-20,20]); % Define colors for Bank 1 and Bank 2 colorBank1 = [0.633, 0.078, 0.243]; colorBank2 = [0.467, 0.675, 0.188]; h1.FaceColor = colorBank1; h2.FaceColor = colorBank2; end leg = legend(["Bank 1", "Bank 2"]); leg.Layout.Tile = "south"; title(t,"Change in Portfolio Value " + scenario + " (%)");
The following plots show the change in portfolio value by percentile in each snapshot. The losses for the Bank 1 are generally highest near the end of the horizon. The gains for Bank 2 are closer to the middle of the horizon. You can interpret the percentile values as a type of value at risk (VaR).
valChangePrctilesUnstack = unstack(valChangePrctiles, ... ["percentChangeVal1_5C_5","percentChangeVal1_5C_50","percentChangeVal1_5C_95","percentChangeVal2C_5","percentChangeVal2C_50","percentChangeVal2C_95"],"Bank"); x = valChangePrctilesUnstack.Snapshot; scenario = "1.5C"; y = table2array(valChangePrctilesUnstack(:,contains(valChangePrctilesUnstack.Properties.VariableNames, matlab.lang.makeValidName("percentChangeVal" + scenario)))); figure; % Plot Bank 1 lines hold on; plot(x, y(:,1), "-", 'LineWidth', 1.5, "Color", colorBank1, "DisplayName", "Bank 1 - 5th percentile"); plot(x, y(:,3), "--", 'LineWidth', 1.5, "Color", colorBank1, "DisplayName", "Bank 1 - 50th percentile"); plot(x, y(:,5), ":", 'LineWidth', 1.5, "Color", colorBank1, "DisplayName", "Bank 1 - 95th percentile"); % Plot Bank 2 lines plot(x, y(:,2), "-", 'LineWidth', 1.5, "Color", colorBank2, "DisplayName", "Bank 2 - 5th percentile"); plot(x, y(:,4), "--", 'LineWidth', 1.5, "Color", colorBank2, "DisplayName", "Bank 2 - 50th percentile"); plot(x, y(:,6), ":", 'LineWidth', 1.5, "Color", colorBank2, "DisplayName", "Bank 2 - 95th percentile"); legend("Location","bestoutside"); xlabel("Snapshot Year"); title("Change in Portfolio Value " + scenario + " (%)", "FontWeight", "normal"); hold off;
The box and whisker plots offer an alternative visualization. The Bank 1 pathways outperform Bank 2 in the short term, especially in the 2C scenario. The separation of distributions becomes clearer further into the future, and the separation occurs more quickly in the 1.5C scenario.
scenario = "1.5C"; scenarioDataName = matlab.lang.makeValidName("percentChangeVal" + scenario); figure; boxchart(valChangeSummary.Snapshot, valChangeSummary.(scenarioDataName), "GroupByColor", valChangeSummary.Bank); colororder([colorBank1; colorBank2]); xlabel("Snapshot Year"); title("Change in Portfolio Value " + scenario + " (%)", "FontWeight", "normal"); legend(["Bank 1", "Bank 2"]);
Conclusion
The methodology used in this example provides a way to calculate the impact of climate scenarios on an obligor's PD. This example uses climate scenarios that have 400 simulations, which allows you to visualize a distribution of portfolio outcomes. The portfolio that is invested in green technologies performs better in the scenarios requiring a faster transition away from fossil fuels.
Methodology
Background
Monasterolo et al provide a methodology for performing a portfolio valuation that accounts for climate scenarios [1]. The methodology first computes a market share shock for different energy sectors based on the scenarios, and then converts this into a shock for the PD of each investment. By aggregating the shocks to the PD, you can calculate the change in value of the portfolio. For more information about this methodology, see [1].
Consider a financial actor endowed with a portfolio of investments through loan contracts. Each investment is represented as a distinct borrower, . Consider three time variables:
Let denote the time at which the portfolio valuation is taken.
Let denote the time at which a climate policy shock potentially occurs.
Let denote the maturity of borrower 's obligation.
In particular, for each borrower ,
.
Let denote the valuation of obligation at time . Then, the valuation of the portfolio at time , , is given by
Assume that the valuation of obligation is based on the expected value of the loan. For each obligor , consider the following:
Let denote the face value of the loan, already including the time-discounting factor.
Let be the probability that the borrower defaults at time given the information available at time .
Let denote the recovery rate of the loan contract.
Then, assume
Climate Policy Shocks
At time , the occurrence of a climate policy shock implies that the economy switches from a business-as-usual scenario characterized by no climate policy, , to a scenario, , where the market shares of some economic sectors are affected. Assume that this transition modifies the PD of the borrower through changes in the market share of the economic sector of . Let denote the change in default probability for obligor when going from scenario to scenario . The change in default probability implies a proportional change in the expected value of the loan:
Next, assume below.
Borrower's Default Conditions
Let denote the total assets of borrower at time in scenario . Let be the shock occurring at time associated with the shift from scenario to scenario . Let denote an exogenous stochastic shock hitting the asset side of the borrower at the maturity date . Then,
Assume that the borrower defaults at time if its net worth, defined as assets minus liabilities, at maturity becomes negative as a result of the two shocks. Let denote the net worth at time in scenario . Note that is independent of the climate scenarios. Let denote the value of obligor 's liabilities, which is assumed to be independent of time and the climate scenarios. Then, the default condition becomes
You can rewrite the default condition as
When , there is no policy shock:
Let be the probability density function of the idiosyncratic shock . Then,
which allows you to express the difference in probability as
Profitability and Shocks on Market Share
Assume that the policy shock impacts the obligor's balance sheet, and thus the expected value of the loan, via a change in the market share of the obligor's economic sector. Let denote the market share at time of sector in the region in scenario . Define similarly. Then the market share shock is defined as
Let denote the elasticity of profitability with respect to market share. A single value is chosen for all obligors. Assume that the relative change in net worth due to the change in scenarios is proportional to the relative market share shock:
Assume that is uniformly distributed with support width and mean . For sufficiently large, you can express the difference in probability as
Then,
The change in PD has been linked to the climate scenarios. However, you need to choose and appropriately. Assume that which you should view as a limitation because could be chosen at the obligor level to reflect empirical data.
Before addressing in more detail, consider the previous equation:
.
The left hand side must be in the range [1,1]. However, the range in practice is actually much smaller. The climate scenarios in this example express a wide range of outcomes, which leads to very large shocks on the right side of the equation. As such, choose a sufficiently large to move those shocks back into the expected range. You cap the shocks to keep them in a more reasonable range. In practice, an institution implementing this methodology decides what shock levels are consistent with their expectations and data.
Next, identify bounds for . To compute as above, you need the interval of integration to be contained in the support of the function , which has been assumed to be . Since you have no view on the direction of the exogenous shock , assume that . It suffices for to be at least as large as The latter term is the absolute value of the shock times the starting book value . It suffices to choose such that
In practice, choose to be equal to the right hand side.
Note that implicitly depends on and and you can choose uniformly across climate scenarios. As reflects the size of the exogenous shocks, ensure that does not depend on the choice of a particular climate scenario.
Finally, note that the quantity appearing in the expression for does not depend on when you choose as above.
Local Functions
importClimateData
The following helper function reads in the downloaded Excel® climate data into a table based on the scenario, energy type, and region.
function climateData = importClimateData(scenario, energy, region) % Construct the path to the scenario folder scenarioFolder = fullfile(pwd, "MITUncertaintyData", scenario); % Find the file with the energy use data filePattern = fullfile(scenarioFolder, "*energy_use_" + energy + "*"); fileStruct = dir(filePattern); file = fileStruct.name; filePath = fullfile(scenarioFolder, file); % Get the sheet names and find the index of the sheet that contains the region sheets = sheetnames(filePath); sheetIdx = contains(sheets, region); sheet = sheets(sheetIdx); % Set up the Import Options and import the data opts = spreadsheetImportOptions("NumVariables", 21); % Specify sheet and range opts.Sheet = sheet; opts.DataRange = "A2:U401"; % Specify column names and types opts.VariableNames = ["Var1", "Var2", "Var3", "VarName4", "VarName5", "VarName6", "VarName7", "VarName8", "VarName9", "VarName10", "VarName11", "VarName12", "VarName13", "VarName14", "VarName15", "VarName16", "VarName17", "VarName18", "VarName19", "VarName20", "VarName21"]; opts.SelectedVariableNames = ["VarName4", "VarName5", "VarName6", "VarName7", "VarName8", "VarName9", "VarName10", "VarName11", "VarName12", "VarName13", "VarName14", "VarName15", "VarName16", "VarName17", "VarName18", "VarName19", "VarName20", "VarName21"]; opts.VariableTypes = ["char", "char", "char", "double", "double", "double", "double", "double", "double", "double", "double", "double", "double", "double", "double", "double", "double", "double", "double", "double", "double"]; % Specify variable properties opts = setvaropts(opts, ["Var1", "Var2", "Var3"], "WhitespaceRule", "preserve"); opts = setvaropts(opts, ["Var1", "Var2", "Var3"], "EmptyFieldRule", "auto"); opts = setvaropts(opts, ["VarName4", "VarName5", "VarName6", "VarName7", "VarName8", "VarName9", "VarName10", "VarName11", "VarName12", "VarName13", "VarName14", "VarName15", "VarName16", "VarName17", "VarName18", "VarName19", "VarName20", "VarName21"], "FillValue", 0); % Import the data climateData = readtable(filePath, opts, "UseExcel", false); clear opts % Reshape and format the data climateData = stack(climateData,2:18,"NewDataVariableName","Energy Use","IndexVariableName","Year"); climateData.Year = renamecats(climateData.Year,["VarName5","VarName6","VarName7","VarName8","VarName9","VarName10","VarName11","VarName12","VarName13","VarName14","VarName15","VarName16","VarName17","VarName18","VarName19","VarName20","VarName21"], ... ["2020", "2025", "2030", "2035", "2040", "2045", "2050", "2055", "2060", "2065", "2070", "2075", "2080", "2085", "2090", "2095", "2100"]); dataHeight = height(climateData); climateData = renamevars(climateData,"VarName4","Simulation"); climateData.Scenario = categorical(repmat(scenario,[dataHeight 1])); climateData.Region = categorical(repmat(region,[dataHeight 1])); climateData.Energy = categorical(repmat(energy,[dataHeight 1])); end
References
[1] Monasterolo, I., Zheng, Jiani I., and S. Battiston. "Climate Transition Risk and Development Finance: A Carbon Risk Assessment of China's Overseas Energy Portfolios." China and World Economy. 26, 6(116–142), 2018. Available at: https://onlinelibrary.wiley.com/doi/epdf/10.1111/cwe.12264.