Published
Edited
Apr 27, 2021
Fork of Untitled
Insert cell
md`# Final Project - Company Research Report
- Cole Barcia CAP6737`
Insert cell
md`** Goal/Intent ** \n \n As a user, I would like a detailed report that will help me visualize relevant valuation metrics as well as supporting trends that would provide a full picture of a companies intrinsic value `
Insert cell
md`** Data Source **`
Insert cell
md`Yahoo Finance has a rich dataset to set up robust data pipelines for visualizations of various financial metrics and top level company data(Selected by Ticker ID). \n
I also intend to look for external data sources to provide growth context of the sector relevant to the company under analysis. This section will likely be unique to a given example company. \n
Lastly, I hope to give reference points of comparison to show how the company is valued compared to other companies with Enterprise Value as the benchmark. `
Insert cell
md`** Work Plan **`
Insert cell
md`
~ 3 Weeks (75% Scope): \n
Yahoo Finance has a rich dataset to set up robust data pipelines for a given company (Ticker ID).
**Valuation** \n
-----Enterprise Value \n
**Management & Backing:** \n
**Company Officers \n
-----Name \n
-----Positions \n
-----Age \n
\n
**Institutional Ownership** \n
-----Institution Name \n
-----Position (% of Company Held) \n
-----Date of Reporting \n
\n
**Insider Ownership** \n
-----Name \n
-----Relation to Company \n
-----# of shares held* \n
\n
**Income Statement** (Total Revenue, Cost of Revenue, Sales_General_Administrative&Marketing_Spend, Operating Income Margin) \n
-----++Quarterly \n
-----++Yearly \n
\n
**Assets, PP&E v Debt** \n
-----++Quarterly \n
-----++Yearly \n
\n
**Revenue v Cost of Revenue, Sales & General Admin, Research & Development Cost** \n
-----++Quarterly \n
-----++Yearly \n
**YahooFinance! Recommendation Engine** \n
-----++ Company Ticker \n
-----++ Recommendation Score \n
~ 1 weeks (25% scope): \n
** Nintendo Deep Dive** \n
This section will likely be unique to a given example company. I intend to look for external data sources on company Investor Relations to provide growth context of the sector relevant to the company under analysis. Lastly I hope to give reference points of comparison to companies with similar business models.


Scope subject to change, improvement, and open to recommendations :)
`
Insert cell
md`**Company Snapshot**`
Insert cell
viewof f1 = text({title: "Enter a Public Company Ticker Symbol", placeholder: "TSLA", description: " Visualizations may not populate if datasets are not available. This can occur with recent IPOs", value: 'NTDOY'})
Insert cell
f1
Insert cell
md`**Body of Report **`
Insert cell
md`** ${f1} : Enterprise Value ** \n\n $ ${ev} `
Insert cell
simpleChart = {

const svg = d3.create('svg').attr('viewBox', [0, 0, width, height]);



svg
.append('circle')
.attr('cx', '45%')
.attr('cy', '55%')
.attr('r', entValue[3]['value']/10000)
.style('fill', '#6e40aa');
svg
.append('circle')
.attr('cx', '15%')
.attr('cy', '55%')
.attr('r', entValue[2]['value']/10000)
.style('fill', 'black');
svg
.append('circle')
.attr('cx', '45%')
.attr('cy', '30%')
.attr('r', entValue[1]['value']/10000)
.style('fill', 'black');
svg
.append('circle')
.attr('cx', '75%')
.attr('cy', '55%')
.attr('r', entValue[0]['value']/10000)
.style('fill', 'black');

return svg.node();
}
Insert cell
ev = {if(Number(summaryQuote.defaultKeyStatistics.enterpriseValue.raw) <0) { return ev1} else {return summaryQuote.defaultKeyStatistics.enterpriseValue.raw }}
Insert cell
md`** ${f1} : What do they do?** \n\n ${summaryQuote.assetProfile.longBusinessSummary} `
Insert cell
summaryQuote = summaryQuote_response.quoteSummary.result[0]
Insert cell
md`

### Company Officers

${
Table(summaryQuote.assetProfile.companyOfficers.map(x=>{
return({ Name: x.name, Title:x.title,Age: x.age});
}))}`
Insert cell
md`

### Institutional Ownership

${
Table(summaryQuote.institutionOwnership.ownershipList.map(x=>{
return({ 'Institution Name': x.organization, 'Company Ownership (%)':x.pctHeld.fmt , 'Position Value ($)': x.value.longFmt, 'Date of Reporting': x.reportDate.fmt});
}))}`
Insert cell
md`

### Insider Ownership

${
Table(summaryQuote.insiderHolders.holders.map(x=>{
return({ 'insider_Name': x.name, 'insider_Relation':x.relation , 'Position Held (Shares)': a5(x,'positionDirect', 'longFmt', 'positionIndirect'), 'Date of Reporting': a5(x,'positionDirectDate', 'fmt', 'positionIndirectDate') });
}))}`
Insert cell
md`**${f1} Financials **`
Insert cell
yearlyBalanceSheet[0].Total_Current_Liabilities
Insert cell

md`

### Income Statement

${
Table(summaryQuote.incomeStatementHistory.incomeStatementHistory.map(x=>{
return({ 'Date': x.endDate.fmt, 'Total Revenue': x.totalRevenue.longFmt , 'Cost of Revenue':x.costOfRevenue.longFmt , 'Gross Profit':x.grossProfit.longFmt , 'Research & Development' : x.researchDevelopment.longFmt , 'Selling & General Administrative Expense' : x.sellingGeneralAdministrative.longFmt });
}))}`
Insert cell
{
const myDiv = html`<div style="width:600px;height:450px;"></div>`;

const layout2 = ({
title: f1 +' Income Statement Breakdown',
xaxis: {
tickfont: {
size: 10,
color: 'rgb(207, 107, 107)'
}
},
yaxis: {
title: summaryQuote.financialData.financialCurrency
},
barmode: 'group'
});
Plotly.newPlot(myDiv, yearlydata, layout2);
return myDiv;
}
Insert cell
dd = select([{label: 'Quarterly', value: 1},{label: 'Yearly', value: 0} ])
Insert cell
duck( dd)
Insert cell
md`

### Balance Sheet Snapshot`
Insert cell
md`

### Balance Sheet

${
Table(summaryQuote.balanceSheetHistory.balanceSheetStatements.map(x=>{
return({ 'Date': x.endDate.fmt, 'Total Current Assets': x.totalCurrentAssets.longFmt , 'Cash':x.cash.longFmt , 'Property, Plant, & Equipment':x.propertyPlantEquipment.longFmt , 'Total Liabilities' : x.totalLiab.longFmt , 'Total Current Liabilities' : x.totalCurrentLiabilities.longFmt });
}))}`
Insert cell
{
const myDiv = html`<div style="width:600px;height:450px;"></div>`;

const layout1 = ({
title: f1 +' Balance Sheet',
xaxis: {
tickfont: {
size: 10,
color: 'rgb(207, 107, 107)'
}
},
yaxis: {
title: summaryQuote.financialData.financialCurrency
},
barmode: 'group'
});
Plotly.newPlot(myDiv, balanceSheetData,layout1);
return myDiv;
}
Insert cell
{
const myDiv = html`<div style="width:600px;height:450px;"></div>`;

const layout1 = ({
title: f1 +' Balance Sheet',
xaxis: {
tickfont: {
size: 10,
color: 'rgb(207, 107, 107)'
}
},
yaxis: {
title: summaryQuote.financialData.financialCurrency
},
barmode: 'group'
});
Plotly.newPlot(myDiv, quarterlyBalanceSheetData,layout1);
return myDiv;
}
Insert cell
{
const myDiv = html`<div style="width:600px;height:450px;"></div>`;

const layout1 = ({
title: f1 +' Cash Flows Yearly',
xaxis: {
tickfont: {
size: 10,
color: 'rgb(207, 107, 107)'
}
},
yaxis: {
title: summaryQuote.financialData.financialCurrency
},
barmode: 'group'
});
Plotly.newPlot(myDiv, cashFlowData ,layout1);
return myDiv;
}
Insert cell
{
const myDiv = html`<div style="width:600px;height:450px;"></div>`;

const layout1 = ({
title: f1 +' Cash Flows Yearly',
xaxis: {
tickfont: {
size: 10,
color: 'rgb(207, 107, 107)'
}
},
yaxis: {
title: summaryQuote.financialData.financialCurrency
},
barmode: 'group'
});
Plotly.newPlot(myDiv, quarterlyCashFlowData ,layout1);
return myDiv;
}
Insert cell
md`**Market Cap Visualization **`
Insert cell
marketcap = shares*price
Insert cell
packchart = {
const root = pack(entValue);
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("font-size", 20)
.attr("font-family", "sans-serif")
.attr("text-anchor", "middle");

const leaf = svg.selectAll("g")
.data(root.leaves())
.join("g")
.attr("transform", d => `translate(${d.x + 1},${d.y + 1})`);


leaf.append("circle")
.attr("r", d => d.r)
;


return svg.node();
}
Insert cell
youMightAlsoLike1 = youMightAlsoLike.sort()
Insert cell
md `**YahooFinance! Recommendation Engine:**`

Insert cell
similarto = {
let badges = [];
badges.push({stat: youMightAlsoLike1[0].score, description: youMightAlsoLike[0].symbol, backColor: "#1a2a41"});
badges.push({stat: youMightAlsoLike1[1].score, description: youMightAlsoLike[1].symbol, backColor: "#22456d"});
badges.push({stat: youMightAlsoLike1[2].score, description: youMightAlsoLike[2].symbol, backColor: "#2d68a1"});
badges.push({stat: youMightAlsoLike1[3].score, description: youMightAlsoLike[3].symbol, backColor: "#4b8ecc"});
badges.push({stat: youMightAlsoLike1[4].score, description: youMightAlsoLike[4].symbol, backColor: "#81b4e4"});


return dashboard({columns: 4, badges});
}
Insert cell
md ` **NINTENDO Data**`
Insert cell
{
const myDiv = html`<div style="width:600px;height:450px;"></div>`;

const layout1 = ({
title: 'Nintendo Revenue Streams',
xaxis: {
tickfont: {
size: 10,
color: 'rgb(207, 107, 107)'
}
},
yaxis: {
title: 'Million JPY'
},
barmode: 'group'
});
Plotly.newPlot(myDiv, nintendoGraph1 ,layout1);
return myDiv;
}
Insert cell
{
const myDiv = html`<div style="width:600px;height:450px;"></div>`;

const layout1 = ({
title: 'Nintendo Off-Device Revenue',
xaxis: {
tickfont: {
size: 10,
color: 'rgb(207, 107, 107)'
}
},
yaxis: {
title: 'Million JPY'
},
barmode: 'group'
});
Plotly.newPlot(myDiv, nintendoGraph ,layout1);
return myDiv;
}
Insert cell
nintendoData = ({
'Net_sales_million_yen' : [504459,489095,1055682,1200560,1308519],
'Gross_profit_million_yen' : [220965,198898,403540,501189,641701],
'Selling_general_and_administrative_expenses_million_yen' : [188083,169535,225983,251488,289331],
'Research_and_development_expenses_million_yen' : [69066,59197,64032,69628,84159],
'Advertising_expenses_million_yen' : [46636,48726,72616,75421,76003],
'Operating_profit_million_yen' : [32881,29362,177557,249701,352370],
'Ordinary_profit_million_yen' : [28790,50364,199356,277355,360461],
'Profit_attributable_to_owners_of_parent_million_yen' : [16505,102574,139590,194009,258641],
'Gross_profit_ratio' : [43.8,40.7,38.2,41.7,49],
'Operating_profit_ratio' : [6.5,6,16.8,20.8,26.9],
'Ordinary_profit_ratio' : [5.7,10.3,18.9,23.1,27.5],
'Profit_attributable_to_owners_of_parent_ratio' : [3.3,21,13.2,16.2,19.8],
'Current_assets_million_yen' : [1014538,1140410,1265929,1344972,1501583],
'Non_current_assets_million_yen' : [282363,328041,367544,345331,432504],
'Total_assets_million_yen' : [1296902,1468452,1633474,1690304,1934087],
'Current_liabilities_million_yen' : [98437,183583,278076,245009,355683],
'Non_current_liabilities_million_yen' : [37563,33895,31822,30496,37503],
'Total_liabilities_million_yen' : [136001,217479,309899,275505,393186],
'Shareholders_equity_million_yen' : [1174118,1262239,1337369,1422260,1575428],
'Total_accumulated_other_comprehensive_income_million_yen' : [-13341,-11399,-18334,-12548,-34741],
'Total_net_assets_million_yen' : [1160901,1250972,1323574,1414798,1540900],
'Capital_adequacy_ratio' : [89.5,85.2,80.8,83.4,79.7],
'Cash_flow_from_operating_activities_million_yen' : [55190,19101,152208,170529,347753],
'Cash_flow_from_investing_activities_million_yen' : [-71740,69518,61387,45353,-188433],
'Cash_flow_from_financing_activities_million_yen' : [-2996,-14435,-61311,-109037,-111031],
'Cash_and_cash_equivalents_ending_million_yen' : [258095,330974,484480,585378,621402],
'Dividend_per_share_Interim_yen' : [30,0,110,170,270],
'Dividend_per_share_Year_end_yen' : [120,430,480,640,820],
'Profit_per_share_yen' : [137.4,853.87,1162.3,1615.51,2171.2],
'Net_assets_per_share_yen' : [9662.73,10412.59,10980.45,11833.91,12933.51],
'Consolidated_payout_ratio' : [109.2,50.4,50.8,50.1,50.2],
'Japan_million_yen' : [135461,130014,261189,265722,301187],
'The_Americas_million_yen' : [225837,203954,441210,528895,565023],
'Europe_million_yen' : [126916,129455,268900,304552,326613],
'Other_million_yen' : [16243,25671,84382,101389,115694],
'Component_ratio' : [73.1,73.4,75.3,77.9,77],
'Dedicated_video_game_hardware_software_million_yen' : [497147,463131,1014631,1152602,1254162],
'Smart_devices_IP_related_income_etc_million_yen' : [5734,24250,39320,46008,51295],
'Playing_cards_etc_million_yen' : [1577,1714,1729,1949,3062],
'Switch_Japan_million_yen' : [0,21565,171739,216307,268078],
'Switch_The_Americas_million_yen' : [0,52415,316854,449362,526557],
'Switch_Europe_million_yen' : [0,29186,193085,271843,314405],
'Switch_Other_million_yen' : [0,7712,71729,90423,110285],
'DS_Japan_million_yen' : [79212,71999,52645,13484,2194],
'DS_The_Americas_million_yen' : [86537,91983,74431,29050,9221],
'DS_Europe_million_yen' : [61391,70359,54037,17112,5983],
'DS_Other_million_yen' : [8743,13607,7154,3387,657]
})
Insert cell
md `**References:**
\n
- Yahoo Finance API call ~ https://observablehq.com/@stroked/yahoofinance
- Dashboard ~ https://observablehq.com/@cobus/dashboard-badges
`

Insert cell
summaryQuote_response = JSON.parse((await (await soFetch(`https://query1.finance.yahoo.com/v10/finance/quoteSummary/${f1}?modules=${modules.join(',')}`)).text()))
Insert cell
summaryQuote_response1 = JSON.parse((await (await soFetch(`https://query1.finance.yahoo.com/v10/finance/quoteSummary/${coolidea[0]}?modules=${modules.join(',')}`)).text()))
Insert cell
summaryQuote_response2 = JSON.parse((await (await soFetch(`https://query1.finance.yahoo.com/v10/finance/quoteSummary/${coolidea[1]}?modules=${modules.join(',')}`)).text()))
Insert cell
summaryQuote_response3 = JSON.parse((await (await soFetch(`https://query1.finance.yahoo.com/v10/finance/quoteSummary/${coolidea[2]}?modules=${modules.join(',')}`)).text()))
Insert cell
summaryQuote_response4 = JSON.parse((await (await soFetch(`https://query1.finance.yahoo.com/v10/finance/quoteSummary/${coolidea[3]}?modules=${modules.join(',')}`)).text()))
Insert cell
summaryQuote_response5 = JSON.parse((await (await soFetch(`https://query1.finance.yahoo.com/v10/finance/quoteSummary/${coolidea[4]}?modules=${modules.join(',')}`)).text()))
Insert cell
import {dashboard} from "@cobus/dashboard-badges"
Insert cell
import {scaleColor, colors} from "@harrystevens/color-scale"
Insert cell
GetPeers =async (symbol)=>{
return JSON.parse(await (await soFetch(`https://query1.finance.yahoo.com/v6/finance/recommendationsbysymbol/${symbol}`)).text()).finance.result[0].recommendedSymbols
}
Insert cell
GetPeers(f1)
Insert cell
md ` **Functions & Variables**`
Insert cell
function deal(yourvariable) { if (typeof(yourvariable) == 'undefined') {
return yourvariable = Number(0);
}else if (yourvariable === 0) { return yourvariable;}else if (typeof(yourvariable) == 'string'){return yourvariable.replace(/,/g, '');}else {yourvariable === Number(0);}
}
Insert cell
Number(dd.value)
Insert cell
function a5(x, prop, fmt, alt) { if (x.hasOwnProperty(prop)) {return x[prop][fmt];} else if (x.hasOwnProperty(alt)){return x[alt][fmt];}
else {return String(2);}}
Insert cell
i = 0;
Insert cell
ev1 = (price*shares) - (summaryQuote.balanceSheetHistory.balanceSheetStatements[0].totalCurrentAssets.raw/109)+(summaryQuote.balanceSheetHistory.balanceSheetStatements[0].totalLiab.raw/109)
Insert cell
shares = summaryQuote.defaultKeyStatistics.sharesOutstanding.raw
Insert cell
price = summaryQuote.financialData.currentPrice.fmt
Insert cell
function makeListNegative(array) {
return array.map(v => '-'+v);
}
Insert cell
coolidea = [youMightAlsoLike[0].symbol,youMightAlsoLike[1].symbol,youMightAlsoLike[2].symbol,youMightAlsoLike[3].symbol,youMightAlsoLike[4].symbol]
Insert cell
md`** Team ** \n\n Party of 1.\n
I prefer to use this as a passion project and take on the additional work
`
Insert cell
md`

### Datasets`
Insert cell
nintendoGraph = [{
type: 'bar',
x: ['2016','2017','2018','2019','2020'],
y: nintendoData.Playing_cards_etc_million_yen,
hovertemplate: '%{y}',
marker: {
color: '#ff7847'
} ,
name: 'Playing_cards_etc_million_yen'
},{
type: 'bar',
x: ['2016','2017','2018','2019','2020'],
y: nintendoData.Smart_devices_IP_related_income_etc_million_yen,
hovertemplate: '%{y}',
marker: {
color: '#e4419d'
},
name: 'Smart_devices_IP_related_income_etc_million_yen'
}
]
Insert cell
nintendoGraph1 = [{
type: 'bar',
x: ['2016','2017','2018','2019','2020'],
y: nintendoData.Switch_The_Americas_million_yen,
hovertemplate: '%{y}',
marker: {
color: '#ff7847'
} ,
name: 'Switch_The_Americas'
},{
type: 'bar',
x: ['2016','2017','2018','2019','2020'],
y: nintendoData.Switch_Europe_million_yen,
hovertemplate: '%{y}',
marker: {
color: '#bf3caf'
},
name: 'Switch_Europe'
},{
type: 'bar',
x: ['2016','2017','2018','2019','2020'],
y: nintendoData.Switch_Japan_million_yen,
hovertemplate: '%{y}',
marker: {
color: '#6e40aa'
},
name: 'Switch_Japan'
},{
type: 'bar',
x: ['2016','2017','2018','2019','2020'],
y: nintendoData.Switch_Other_million_yen,
hovertemplate: '%{y}',
marker: {
color: '#6e40aa'
},
name: 'Switch_Other'
},{
type: 'bar',
x: ['2016','2017','2018','2019','2020'],
y: nintendoData.Smart_devices_IP_related_income_etc_million_yen,
hovertemplate: '%{y}',
marker: {
color: '#4c6edb'
},
name: 'Smart_devices_IP_related_income_etc'
},{
type: 'bar',
x: ['2016','2017','2018','2019','2020'],
y: nintendoData.Playing_cards_etc_million_yen,
hovertemplate: '%{y}',
marker: {
color: '#1ddfa3'
} ,
name: 'Playing_cards_etc'
},
{
type: 'bar',
x: ['2016','2017','2018','2019','2020'],
y: nintendoData.DS_The_Americas_million_yen,
marker: {
color: '#c6d63c'
} ,
name: '3DS_The_Americas'
},{
type: 'bar',
x: ['2016','2017','2018','2019','2020'],
y: nintendoData.DS_Europe_million_yen,
marker: {
color: '#80cdc1'
},
name: '3DS_Europe'
},{
type: 'bar',
x: ['2016','2017','2018','2019','2020'],
y: nintendoData.DS_Japan_million_yen,
marker: {
color: '#af7aa1'
},
name: '3DS_Japan'
},{
type: 'bar',
x: ['2016','2017','2018','2019','2020'],
y: nintendoData.DS_Other_million_yen,
marker: {
color: '#ff9da7'
},
name: '3DS_Other'
}
]
Insert cell
youMightAlsoLike = GetPeers(f1);
Insert cell
quarterlyBalanceSheet = summaryQuote.balanceSheetHistoryQuarterly.balanceSheetStatements.map(x=>{
return({ 'Date': x.endDate.fmt, 'Total_Current_Assets': x.totalCurrentAssets.longFmt , 'Cash':x.cash.longFmt , 'Property_Plant_Equipment':deal(x.propertyPlantEquipment.longFmt) , 'Total_Liabilities' : x.totalLiab.longFmt , 'Total_Current_Liabilities' : x.totalCurrentLiabilities.longFmt, 'Total_Current_Liabilities' : x.totalCurrentLiabilities.longFmt });
})
Insert cell
yearlyBalanceSheet = summaryQuote.balanceSheetHistory.balanceSheetStatements.map(x=>{
return({ 'Date': x.endDate.fmt, 'Total_Current_Assets': x.totalCurrentAssets.longFmt , 'Cash':x.cash.longFmt , 'Property_Plant_Equipment':deal(x.propertyPlantEquipment.longFmt) , 'Total_Liabilities' : x.totalLiab.longFmt , 'Total_Current_Liabilities' : x.totalCurrentLiabilities.longFmt, 'Total_Current_Liabilities' : x.totalCurrentLiabilities.longFmt });
})
Insert cell
yearlyCashFlow = summaryQuote.cashflowStatementHistory.cashflowStatements.map(x=>{
return({ 'Date': x.endDate.fmt, 'Total_Cash_From_Operating_Activities': deal(x.totalCashFromOperatingActivities.longFmt) , 'Total_Cash_Flows_From_Investing_Activities':deal(x.totalCashflowsFromInvestingActivities.longFmt) , 'Total_Cash_From_Financing_Activities':deal(x.totalCashFromFinancingActivities.longFmt) });
})
Insert cell
quarterlyCashFlow = summaryQuote.cashflowStatementHistoryQuarterly.cashflowStatements.map(x=>{
return({ 'Date': x.endDate.fmt, 'Total_Cash_From_Operating_Activities': deal(x.totalCashFromOperatingActivities.longFmt) , 'Total_Cash_Flows_From_Investing_Activities':deal(x.totalCashflowsFromInvestingActivities.longFmt) , 'Total_Cash_From_Financing_Activities':deal(x.totalCashFromFinancingActivities.longFmt) });
})
Insert cell
entValue = [{ id: 'Small Cap' , value: Math.sqrt(1000000000), color:'blue'},{ id: 'Medium Cap' , value: Math.sqrt(10000000000), color:'blue'},{ id: 'Large Cap' , value: Math.sqrt(100000000000), color:'blue'},{ id: f1 , value: Math.sqrt(marketcap), color:"purple"}]
Insert cell
yearlyIncomeStatement = summaryQuote.incomeStatementHistory.incomeStatementHistory.map(x=>{
return({ 'Date': x.endDate.fmt, 'total_Revenue': x.totalRevenue.longFmt , 'cost_of_Revenue':x.costOfRevenue.longFmt , 'gross_Profit':x.grossProfit.longFmt , 'research_N_Development' : deal(x.researchDevelopment.longFmt) , 'selling_General_Admin_Expense' : x.sellingGeneralAdministrative.longFmt });
})
Insert cell
quarterlyIncomeStatement = summaryQuote.incomeStatementHistoryQuarterly.incomeStatementHistory.map(x=>{
return({ 'Date': x.endDate.fmt, 'total_Revenue': x.totalRevenue.longFmt , 'cost_of_Revenue':x.costOfRevenue.longFmt , 'gross_Profit':x.grossProfit.longFmt , 'research_N_Development' : x.researchDevelopment.longFmt , 'selling_General_Admin_Expense' : x.sellingGeneralAdministrative.longFmt });
})
Insert cell
function duck(dd) { if (Number(dd.value) ===1){ const myDiv = html`<div style="width:600px;height:450px;"></div>`;
const layout2 = ({
title: f1 +' Income Statement Breakdown',
xaxis: {
tickfont: {
size: 10,
color: 'rgb(207, 107, 107)'
}
},
yaxis: {
title: summaryQuote.financialData.financialCurrency
},
barmode: 'group'
});
Plotly.newPlot(myDiv, quarterlydata, layout2); return myDiv;}
else{ const myDiv = html`<div style="width:600px;height:450px;"></div>`;
const layout2 = ({
title: f1 +' Income Statement Breakdown',
xaxis: {
tickfont: {
size: 10,
color: 'rgb(207, 107, 107)'
}
},
yaxis: {
title: 'USD ($)'
},
barmode: 'group'
});
Plotly.newPlot(myDiv, yearlydata, layout2);return myDiv;}};
Insert cell
yearlydata = [
{
type: 'bar',
x: [yearlyIncomeStatement[0].Date,yearlyIncomeStatement[1].Date,yearlyIncomeStatement[2].Date,yearlyIncomeStatement[3].Date],
y: [yearlyIncomeStatement[0].total_Revenue,yearlyIncomeStatement[1].total_Revenue,yearlyIncomeStatement[2].total_Revenue,yearlyIncomeStatement[3].total_Revenue],
hovertemplate: '%{y}',
marker: {
color: '#6e40aa'
},
name: 'Total Revenue'
},
{
type: 'bar',
x: [yearlyIncomeStatement[0].Date,yearlyIncomeStatement[1].Date,yearlyIncomeStatement[2].Date,yearlyIncomeStatement[3].Date],
y: makeListNegative([yearlyIncomeStatement[0].cost_of_Revenue,yearlyIncomeStatement[1].cost_of_Revenue,yearlyIncomeStatement[2].cost_of_Revenue,yearlyIncomeStatement[3].cost_of_Revenue]) ,
base: 0,
marker: {
color: '#e4419d'
},
name: 'Cost of Revenue'
},
{
type: 'bar',
x: [yearlyIncomeStatement[0].Date,yearlyIncomeStatement[1].Date,yearlyIncomeStatement[2].Date,yearlyIncomeStatement[3].Date],
y: makeListNegative([yearlyIncomeStatement[0].research_N_Development,yearlyIncomeStatement[1].research_N_Development,yearlyIncomeStatement[2].research_N_Development,yearlyIncomeStatement[3].research_N_Development]) ,
base: 0,
marker: {
color: '#ff7847'
},
name: 'Research & Development'
},
{
type: 'bar',
x: [yearlyIncomeStatement[0].Date,yearlyIncomeStatement[1].Date,yearlyIncomeStatement[2].Date,yearlyIncomeStatement[3].Date],
y: makeListNegative([yearlyIncomeStatement[0].selling_General_Admin_Expense,yearlyIncomeStatement[1].selling_General_Admin_Expense,yearlyIncomeStatement[2].selling_General_Admin_Expense,yearlyIncomeStatement[3].selling_General_Admin_Expense]) ,
base: 0,
marker: {
color: '#bf3caf'
},
name: 'Selling & General Administrative Expense'
},
{
type: 'scatter',
x: [yearlyIncomeStatement[0].Date,yearlyIncomeStatement[1].Date,yearlyIncomeStatement[2].Date,yearlyIncomeStatement[3].Date],
y: [parseFloat(yearlyIncomeStatement[0].total_Revenue.replace(/,/g, '')) - (parseFloat(yearlyIncomeStatement[0].selling_General_Admin_Expense.replace(/,/g, '')) + parseFloat(deal(yearlyIncomeStatement[0].research_N_Development)) + parseFloat(yearlyIncomeStatement[0].cost_of_Revenue.replace(/,/g, ''))),parseFloat(yearlyIncomeStatement[1].total_Revenue.replace(/,/g, '')) - (parseFloat(yearlyIncomeStatement[1].selling_General_Admin_Expense.replace(/,/g, '')) + parseFloat(deal(yearlyIncomeStatement[1].research_N_Development)) + parseFloat(yearlyIncomeStatement[1].cost_of_Revenue.replace(/,/g, ''))),parseFloat(yearlyIncomeStatement[2].total_Revenue.replace(/,/g, '')) - (parseFloat(yearlyIncomeStatement[2].selling_General_Admin_Expense.replace(/,/g, '')) + parseFloat(deal(yearlyIncomeStatement[2].research_N_Development)) + parseFloat(yearlyIncomeStatement[2].cost_of_Revenue.replace(/,/g, ''))),parseFloat(yearlyIncomeStatement[3].total_Revenue.replace(/,/g, '')) - (parseFloat(yearlyIncomeStatement[3].selling_General_Admin_Expense.replace(/,/g, '')) + parseFloat(deal(yearlyIncomeStatement[3].research_N_Development)) + parseFloat(yearlyIncomeStatement[3].cost_of_Revenue.replace(/,/g, '')))] ,
base: 0,
marker: {
color: '#1ddfa3'
},
name: 'Operating Income Margin'
}];
Insert cell
quarterlydata = [
{
type: 'bar',
x: [quarterlyIncomeStatement[0].Date,quarterlyIncomeStatement[1].Date,quarterlyIncomeStatement[2].Date,quarterlyIncomeStatement[3].Date],
y: [quarterlyIncomeStatement[0].total_Revenue,quarterlyIncomeStatement[1].total_Revenue,quarterlyIncomeStatement[2].total_Revenue,quarterlyIncomeStatement[3].total_Revenue],
hovertemplate: '%{y}',
marker: {
color: '#6e40aa'
},
name: 'Total Revenue'
},
{
type: 'bar',
x: [quarterlyIncomeStatement[0].Date,quarterlyIncomeStatement[1].Date,quarterlyIncomeStatement[2].Date,quarterlyIncomeStatement[3].Date],
y: makeListNegative([quarterlyIncomeStatement[0].cost_of_Revenue,quarterlyIncomeStatement[1].cost_of_Revenue,quarterlyIncomeStatement[2].cost_of_Revenue,quarterlyIncomeStatement[3].cost_of_Revenue]) ,
base: 0,
marker: {
color: '#e4419d'
},
name: 'Cost of Revenue'
},
{
type: 'bar',
x: [quarterlyIncomeStatement[0].Date,quarterlyIncomeStatement[1].Date,quarterlyIncomeStatement[2].Date,quarterlyIncomeStatement[3].Date],
y: makeListNegative([quarterlyIncomeStatement[0].research_N_Development,quarterlyIncomeStatement[1].research_N_Development,quarterlyIncomeStatement[2].research_N_Development,quarterlyIncomeStatement[3].research_N_Development]) ,
base: 0,
marker: {
color: '#ff7847'
},
name: 'Research & Development'
},
{
type: 'bar',
x: [quarterlyIncomeStatement[0].Date,quarterlyIncomeStatement[1].Date,quarterlyIncomeStatement[2].Date,quarterlyIncomeStatement[3].Date],
y: makeListNegative([quarterlyIncomeStatement[0].selling_General_Admin_Expense,quarterlyIncomeStatement[1].selling_General_Admin_Expense,quarterlyIncomeStatement[2].selling_General_Admin_Expense,quarterlyIncomeStatement[3].selling_General_Admin_Expense]) ,
base: 0,
marker: {
color: '#bf3caf'
},
name: 'Selling & General Administrative Expense'
},
{
type: 'scatter',
x: [quarterlyIncomeStatement[0].Date,quarterlyIncomeStatement[1].Date,quarterlyIncomeStatement[2].Date,quarterlyIncomeStatement[3].Date],
y: [parseFloat(quarterlyIncomeStatement[0].total_Revenue.replace(/,/g, '')) - (parseFloat(quarterlyIncomeStatement[0].selling_General_Admin_Expense.replace(/,/g, '')) + parseFloat(deal(quarterlyIncomeStatement[0].research_N_Development)) + parseFloat(quarterlyIncomeStatement[0].cost_of_Revenue.replace(/,/g, ''))),parseFloat(quarterlyIncomeStatement[1].total_Revenue.replace(/,/g, '')) - (parseFloat(quarterlyIncomeStatement[1].selling_General_Admin_Expense.replace(/,/g, '')) + parseFloat(deal(quarterlyIncomeStatement[1].research_N_Development)) + parseFloat(quarterlyIncomeStatement[1].cost_of_Revenue.replace(/,/g, ''))),parseFloat(quarterlyIncomeStatement[2].total_Revenue.replace(/,/g, '')) - (parseFloat(quarterlyIncomeStatement[2].selling_General_Admin_Expense.replace(/,/g, '')) + parseFloat(deal(quarterlyIncomeStatement[2].research_N_Development)) + parseFloat(quarterlyIncomeStatement[2].cost_of_Revenue.replace(/,/g, ''))),parseFloat(quarterlyIncomeStatement[3].total_Revenue.replace(/,/g, '')) - (parseFloat(quarterlyIncomeStatement[3].selling_General_Admin_Expense.replace(/,/g, '')) + parseFloat(deal(quarterlyIncomeStatement[3].research_N_Development)) + parseFloat(deal(quarterlyIncomeStatement[3].cost_of_Revenue)))] ,
base: 0,
marker: {
color: '#1ddfa3'
},
name: 'Operating Income Margin'
}];
Insert cell
quarterlyBalanceSheetData = [
{
type: 'bar',
x: [quarterlyBalanceSheet[0].Date,quarterlyBalanceSheet[1].Date,quarterlyBalanceSheet[2].Date,quarterlyBalanceSheet[3].Date],
y: [quarterlyBalanceSheet[0].Total_Current_Assets,quarterlyBalanceSheet[1].Total_Current_Assets,quarterlyBalanceSheet[2].Total_Current_Assets,quarterlyBalanceSheet[3].Total_Current_Assets],
hovertemplate: '%{y}',
marker: {
color: '#6e40aa'
},
name: 'Current Assets'
},
{
type: 'bar',
x: [quarterlyBalanceSheet[0].Date,quarterlyBalanceSheet[1].Date,quarterlyBalanceSheet[2].Date,quarterlyBalanceSheet[3].Date],
y:makeListNegative([quarterlyBalanceSheet[0].Total_Liabilities,quarterlyBalanceSheet[1].Total_Liabilities,quarterlyBalanceSheet[2].Total_Liabilities,quarterlyBalanceSheet[3].Total_Liabilities]),
base: 0,
marker: {
color: '#e4419d'
},
name: 'Liabilities'
},
{
type: 'bar',
x: [quarterlyBalanceSheet[0].Date,quarterlyBalanceSheet[1].Date,quarterlyBalanceSheet[2].Date,quarterlyBalanceSheet[3].Date],
y:[quarterlyBalanceSheet[0].Property_Plant_Equipment,quarterlyBalanceSheet[1].Property_Plant_Equipment,quarterlyBalanceSheet[2].Property_Plant_Equipment,quarterlyBalanceSheet[3].Property_Plant_Equipment],
base: 0,
marker: {
color: '#ff7847'
},
name: 'Property, Plant & Equipment'
}
];
Insert cell
balanceSheetData = [
{
type: 'bar',
x: [yearlyBalanceSheet[0].Date,yearlyBalanceSheet[1].Date,yearlyBalanceSheet[2].Date,yearlyBalanceSheet[3].Date],
y: [yearlyBalanceSheet[0].Total_Current_Assets,yearlyBalanceSheet[1].Total_Current_Assets,yearlyBalanceSheet[2].Total_Current_Assets,yearlyBalanceSheet[3].Total_Current_Assets],
hovertemplate: '%{y}',
marker: {
color: '#6e40aa'
},
name: 'Current Assets'
},
{
type: 'bar',
x: [yearlyBalanceSheet[0].Date,yearlyBalanceSheet[1].Date,yearlyBalanceSheet[2].Date,yearlyBalanceSheet[3].Date],
y:makeListNegative([yearlyBalanceSheet[0].Total_Liabilities,yearlyBalanceSheet[1].Total_Liabilities,yearlyBalanceSheet[2].Total_Liabilities,yearlyBalanceSheet[3].Total_Liabilities]),
base: 0,
marker: {
color: '#e4419d'
},
name: 'Liabilities'
},
{
type: 'bar',
x: [yearlyBalanceSheet[0].Date,yearlyBalanceSheet[1].Date,yearlyBalanceSheet[2].Date,yearlyBalanceSheet[3].Date],
y:[yearlyBalanceSheet[0].Property_Plant_Equipment,yearlyBalanceSheet[1].Property_Plant_Equipment,yearlyBalanceSheet[2].Property_Plant_Equipment,yearlyBalanceSheet[3].Property_Plant_Equipment],
base: 0,
marker: {
color: '#ff7847'
},
name: 'Property, Plant & Equipment'
}
];
Insert cell
cashFlowData = [
{
type: 'bar',
x: [yearlyCashFlow[0].Date,yearlyCashFlow[1].Date,yearlyCashFlow[2].Date,yearlyCashFlow[3].Date],
y: [yearlyCashFlow[0].Total_Cash_From_Operating_Activities,yearlyCashFlow[1].Total_Cash_From_Operating_Activities,yearlyCashFlow[2].Total_Cash_From_Operating_Activities,yearlyCashFlow[3].Total_Cash_From_Operating_Activities],
hovertemplate: '%{y}',
marker: {
color: '#6e40aa'
},
name: 'Operating Cash Flow'
},
{
type: 'bar',
x: [yearlyCashFlow[0].Date,yearlyCashFlow[1].Date,yearlyCashFlow[2].Date,yearlyCashFlow[3].Date],
y:[yearlyCashFlow[0].Total_Cash_Flows_From_Investing_Activities,yearlyCashFlow[1].Total_Cash_Flows_From_Investing_Activities,yearlyCashFlow[2].Total_Cash_Flows_From_Investing_Activities,yearlyCashFlow[3].Total_Cash_Flows_From_Investing_Activities],
base: 0,
marker: {
color: '#e4419d'
},
name: 'Investing Cash Flow'
},
{
type: 'bar',
x: [yearlyCashFlow[0].Date,yearlyCashFlow[1].Date,yearlyCashFlow[2].Date,yearlyCashFlow[3].Date],
y:[yearlyCashFlow[0].Total_Cash_From_Financing_Activities,yearlyCashFlow[1].Total_Cash_From_Financing_Activities,yearlyCashFlow[2].Total_Cash_From_Financing_Activities,yearlyCashFlow[3].Total_Cash_From_Financing_Activities],
base: 0,
marker: {
color: '#ff7847'
},
name: 'Financing Cash Flow'
},
{
type: 'scatter',
x: [yearlyCashFlow[0].Date,yearlyCashFlow[1].Date,yearlyCashFlow[2].Date,yearlyCashFlow[3].Date],
y: [parseFloat(deal(yearlyCashFlow[0].Total_Cash_From_Operating_Activities.replace(/,/g, ''))) + parseFloat(deal(yearlyCashFlow[0].Total_Cash_Flows_From_Investing_Activities.replace(/,/g, ''))) + parseFloat(deal(yearlyCashFlow[0].Total_Cash_From_Financing_Activities.replace(/,/g, ''))) ,parseFloat(yearlyCashFlow[1].Total_Cash_From_Operating_Activities.replace(/,/g, '')) + parseFloat(yearlyCashFlow[1].Total_Cash_Flows_From_Investing_Activities.replace(/,/g, '')) + parseFloat(deal(yearlyCashFlow[1].Total_Cash_From_Financing_Activities.replace(/,/g, ''))) ,parseFloat(deal(yearlyCashFlow[2].Total_Cash_From_Operating_Activities.replace(/,/g, ''))) + parseFloat(deal(yearlyCashFlow[2].Total_Cash_Flows_From_Investing_Activities.replace(/,/g, ''))) + parseFloat(deal(yearlyCashFlow[2].Total_Cash_From_Financing_Activities.replace(/,/g, ''))), parseFloat(deal(yearlyCashFlow[3].Total_Cash_From_Operating_Activities.replace(/,/g, ''))) + parseFloat(deal(yearlyCashFlow[3].Total_Cash_Flows_From_Investing_Activities.replace(/,/g, ''))) + parseFloat(deal(yearlyCashFlow[3].Total_Cash_From_Financing_Activities.replace(/,/g, '')))] ,
base: 0,
marker: {
color: '#1ddfa3'
},
name: 'Net Cash Flow'}
];
Insert cell
quarterlyCashFlowData = [
{
type: 'bar',
x: [quarterlyCashFlow[0].Date,quarterlyCashFlow[1].Date,quarterlyCashFlow[2].Date,quarterlyCashFlow[3].Date],
y: [quarterlyCashFlow[0].Total_Cash_From_Operating_Activities,quarterlyCashFlow[1].Total_Cash_From_Operating_Activities,quarterlyCashFlow[2].Total_Cash_From_Operating_Activities,quarterlyCashFlow[3].Total_Cash_From_Operating_Activities],
hovertemplate: '%{y}',
marker: {
color: '#6e40aa'
},
name: 'Operating Cash Flow'
},
{
type: 'bar',
x: [quarterlyCashFlow[0].Date,quarterlyCashFlow[1].Date,quarterlyCashFlow[2].Date,quarterlyCashFlow[3].Date],
y:[quarterlyCashFlow[0].Total_Cash_Flows_From_Investing_Activities,quarterlyCashFlow[1].Total_Cash_Flows_From_Investing_Activities,quarterlyCashFlow[2].Total_Cash_Flows_From_Investing_Activities,quarterlyCashFlow[3].Total_Cash_Flows_From_Investing_Activities],
base: 0,
marker: {
color: '#e4419d'
},
name: 'Investing Cash Flow'
},
{
type: 'bar',
x: [quarterlyCashFlow[0].Date,quarterlyCashFlow[1].Date,quarterlyCashFlow[2].Date,quarterlyCashFlow[3].Date],
y:[quarterlyCashFlow[0].Total_Cash_From_Financing_Activities,quarterlyCashFlow[1].Total_Cash_From_Financing_Activities,quarterlyCashFlow[2].Total_Cash_From_Financing_Activities,quarterlyCashFlow[3].Total_Cash_From_Financing_Activities],
base: 0,
marker: {
color: '#ff7847'
},
name: 'Financing Cash Flow'
},
{
type: 'scatter',
x: [quarterlyCashFlow[0].Date,quarterlyCashFlow[1].Date,quarterlyCashFlow[2].Date,quarterlyCashFlow[3].Date],
y: [parseFloat(deal(quarterlyCashFlow[0].Total_Cash_From_Operating_Activities.replace(/,/g, ''))) + parseFloat(deal(quarterlyCashFlow[0].Total_Cash_Flows_From_Investing_Activities.replace(/,/g, ''))) + parseFloat(deal(quarterlyCashFlow[0].Total_Cash_From_Financing_Activities.replace(/,/g, ''))) ,parseFloat(quarterlyCashFlow[1].Total_Cash_From_Operating_Activities.replace(/,/g, '')) + parseFloat(quarterlyCashFlow[1].Total_Cash_Flows_From_Investing_Activities.replace(/,/g, '')) + parseFloat(deal(quarterlyCashFlow[1].Total_Cash_From_Financing_Activities.replace(/,/g, ''))) ,parseFloat(deal(quarterlyCashFlow[2].Total_Cash_From_Operating_Activities.replace(/,/g, ''))) + parseFloat(deal(quarterlyCashFlow[2].Total_Cash_Flows_From_Investing_Activities.replace(/,/g, ''))) + parseFloat(deal(quarterlyCashFlow[2].Total_Cash_From_Financing_Activities.replace(/,/g, ''))), parseFloat(deal(quarterlyCashFlow[3].Total_Cash_From_Operating_Activities.replace(/,/g, ''))) + parseFloat(deal(quarterlyCashFlow[3].Total_Cash_Flows_From_Investing_Activities.replace(/,/g, ''))) + parseFloat(deal(quarterlyCashFlow[3].Total_Cash_From_Financing_Activities.replace(/,/g, '')))] ,
base: 0,
marker: {
color: '#1ddfa3'
},
name: 'Net Cash Flow'}
];
Insert cell
parseFloat(yearlyIncomeStatement[0].total_Revenue.replace(/,/g, '')) - (parseFloat(yearlyIncomeStatement[0].selling_General_Admin_Expense.replace(/,/g, '')) + parseFloat(deal(yearlyIncomeStatement[0].research_N_Development)) + parseFloat(yearlyIncomeStatement[0].cost_of_Revenue.replace(/,/g, '')))
Insert cell
yearlyIncomeStatement[0].research_N_Development
Insert cell
layout = ({
title: f1 +' Balance Sheet',
xaxis: {
tickfont: {
size: 10,
color: 'rgb(207, 107, 107)'
}
},
yaxis: {
title: 'USD ($)'
},
barmode: 'stack'
});
Insert cell
md`**Library & Function Import **`
Insert cell
d3 = require("d3") // Use the latest Version
Insert cell
Plotly = require("https://cdn.plot.ly/plotly-latest.min.js")
Insert cell
import {radio, slider, select, text, checkbox, button} from "@jashkenas/inputs"
Insert cell
import { soFetch } from '@alecglassford/so-fetch'
Insert cell
import { Table } from "@observablehq/table"
Insert cell
modules = [ 'assetProfile', 'incomeStatementHistory', 'incomeStatementHistoryQuarterly', 'balanceSheetHistory', 'balanceSheetHistoryQuarterly', 'cashFlowStatementHistory', 'cashFlowStatementHistoryQuarterly', 'defaultKeyStatistics', 'financialData', 'calendarEvents', 'secFilings', 'recommendationTrend', 'upgradeDowngradeHistory', 'institutionOwnership', 'fundOwnership', 'majorDirectHolders', 'majorHoldersBreakdown', 'insiderTransactions', 'insiderHolders', 'netSharePurchaseActivity', 'earnings', 'earningsHistory', 'earningsTrend', 'industryTrend', 'indexTrend', 'sectorTrend' ]
Insert cell
import {format, height, width} from '@d3/bubble-chart'
Insert cell
{
const myDiv = html`<div style="width:600px;height:450px;"></div>`;

const dates = appleLast30daysData.map(d => d.date);

const highs = appleLast30daysData.map(d => d.high);
const lows = appleLast30daysData.map(d => d.low);

const trace1 = {
type: "scatter",
mode: "lines",
name: 'AAPL High',
x: dates,
y: highs,
line: { color: '#17BECF' }
};

const trace2 = {
type: "scatter",
mode: "lines",
name: 'AAPL Low',
x: dates,
y: lows,
line: { color: '#7F7F7F' }
};

const data = [trace1, trace2];

const layout = {
title: 'AAPL stock price Last 30 days of ',
title: 'Custom Range',

xaxis: {
//range: ["2021-01-01", "2021-01-31"],
type: 'date'
},
yaxis: {
//range: [120, 150],
type: 'linear'
}
};
Plotly.newPlot(myDiv, data, layout);
return myDiv;
}
Insert cell
getQuoteHistory = async (symbol, days) => {
const startDate = new Date();
startDate.setDate(startDate.getDate() - days);
return await (await soFetch(
`https://query1.finance.yahoo.com/v8/finance/chart/?symbol=${f1}&period1=${Math.floor(
startDate.getTime() / 1000
)}&period2=${Date.now()}&interval=1d`
)).json();
}
Insert cell
tickerHistory = getTickerHistoryData(f1, 30);
Insert cell
md`**Data Portal **`
Insert cell
Table(tickerHistory)
Insert cell
getTickerHistoryData = async (ticker, days) => {
const response = await getQuoteHistory(ticker, days);
// Combine timestamp, high, close, open, low, volume, adjusted close
const timestamp = response.chart.result[0].timestamp;
const quote = response.chart.result[0].indicators.quote[0];
return timestamp.map((time, i) => ({
date: d3.timeFormat("%x")(d3.timeParse("%s")(time)),
volume: quote.volume[i],
high: quote.high[i],
close: quote.close[i],
low: quote.low[i],
open: quote.open[i]
}));
}
Insert cell
getQuote = async symbol => {
const data = await (await soFetch(
`https://query1.finance.yahoo.com/v7/finance/quote?symbols=${symbol}`
)).json();
return data.quoteResponse.result[0];
}
Insert cell
pack = entValue => d3.pack()
.size([width - 2, height - 2])
.padding(3)
(d3.hierarchy({children: entValue})
.sum(d => d.value))
Insert cell
data = {
// Combine timestamp, high, close, open, low, volume, adjusted close
const timestamp = tickerHistory.chart.result[0].timestamp;
const quote = tickerHistory.chart.result[0].indicators.quote[0];
return timestamp.map((time, i) => ({
date: d3.timeFormat("%x")(d3.timeParse("%s")(time)),
volume: quote.volume[i],
high: quote.high[i],
close: quote.close[i],
low: quote.low[i],
open: quote.open[i]
}));
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more