[{"data":1,"prerenderedAt":2134},["ShallowReactive",2],{"doc:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fcorrel-and-linest-equivalents-in-python":3,"surround:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fcorrel-and-linest-equivalents-in-python":2100,"ancestors:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fcorrel-and-linest-equivalents-in-python":2109,"children:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fcorrel-and-linest-equivalents-in-python":2114,"related:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fcorrel-and-linest-equivalents-in-python":2115},{"id":4,"title":5,"body":6,"dateModified":2067,"datePublished":2067,"description":2068,"extension":2069,"faq":2070,"keyword":2079,"kind":2080,"meta":2081,"navigation":134,"path":2089,"seo":2090,"slug":2093,"stem":2094,"topics":2095,"type":2098,"__hash__":2099},"docs\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fcorrel-and-linest-equivalents-in-python\u002Findex.md","CORREL and LINEST Equivalents in Python",{"type":7,"value":8,"toc":2053},"minimark",[9,28,33,67,93,196,200,214,327,346,352,482,486,505,607,615,619,625,731,734,808,821,825,836,1326,1333,1464,1468,1471,1525,1737,1748,1752,1757,1866,1873,1877,1892,1896,1946,1950,1980,1984,1998,2004,2010,2016,2020,2049],[10,11,12,13,17,18,21,22,27],"p",{},"Two of Excel's statistics functions do a lot of quiet work in reporting spreadsheets. ",[14,15,16],"code",{},"CORREL"," says how strongly two columns move together — ad spend and sales, temperature and energy use. ",[14,19,20],{},"LINEST"," fits a straight line (or a plane, with several inputs) and returns the coefficients plus a block of statistics that tells you how much to trust them. Moving these calculations to Python means knowing which pandas or NumPy call gives the same number, and knowing LINEST's quirks well enough to reproduce its output exactly — including the one that surprises everybody, its reversed coefficient order. This page is part of ",[23,24,26],"a",{"href":25},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002F","Excel Formula Equivalents in pandas",".",[29,30,32],"h2",{"id":31},"prerequisites","Prerequisites",[34,35,40],"pre",{"className":36,"code":37,"language":38,"meta":39,"style":39},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas numpy scipy openpyxl\n","bash","",[14,41,42],{"__ignoreMap":39},[43,44,47,51,55,58,61,64],"span",{"class":45,"line":46},"line",1,[43,48,50],{"class":49},"sMTad","pip",[43,52,54],{"class":53},"srMev"," install",[43,56,57],{"class":53}," pandas",[43,59,60],{"class":53}," numpy",[43,62,63],{"class":53}," scipy",[43,65,66],{"class":53}," openpyxl\n",[10,68,69,72,73,75,76,79,80,75,83,86,87,75,89,92],{},[14,70,71],{},"ads.xlsx"," holds twelve months of marketing data: TV spend and online spend (in thousands) and sales (also in thousands). The workbook also contains the spreadsheet versions of the calculations — ",[14,74,16],{}," in ",[14,77,78],{},"F1",", ",[14,81,82],{},"RSQ",[14,84,85],{},"F2"," and a full ",[14,88,20],{},[14,90,91],{},"F4:H8"," — so the Python results can be checked against them.",[34,94,98],{"className":95,"code":96,"language":97,"meta":39,"style":39},"language-python shiki shiki-themes github-light github-dark-high-contrast","import numpy as np\nimport pandas as pd\n\ndf = pd.read_excel(\"ads.xlsx\", sheet_name=\"Ads\", usecols=\"A:D\")\nprint(df.to_string(index=False))\n","python",[14,99,100,116,129,136,175],{"__ignoreMap":39},[43,101,102,106,110,113],{"class":45,"line":46},[43,103,105],{"class":104},"s-kum","import",[43,107,109],{"class":108},"skGVy"," numpy ",[43,111,112],{"class":104},"as",[43,114,115],{"class":108}," np\n",[43,117,119,121,124,126],{"class":45,"line":118},2,[43,120,105],{"class":104},[43,122,123],{"class":108}," pandas ",[43,125,112],{"class":104},[43,127,128],{"class":108}," pd\n",[43,130,132],{"class":45,"line":131},3,[43,133,135],{"emptyLinePlaceholder":134},true,"\n",[43,137,139,142,145,148,151,153,157,159,162,164,167,169,172],{"class":45,"line":138},4,[43,140,141],{"class":108},"df ",[43,143,144],{"class":104},"=",[43,146,147],{"class":108}," pd.read_excel(",[43,149,150],{"class":53},"\"ads.xlsx\"",[43,152,79],{"class":108},[43,154,156],{"class":155},"sa561","sheet_name",[43,158,144],{"class":104},[43,160,161],{"class":53},"\"Ads\"",[43,163,79],{"class":108},[43,165,166],{"class":155},"usecols",[43,168,144],{"class":104},[43,170,171],{"class":53},"\"A:D\"",[43,173,174],{"class":108},")\n",[43,176,178,182,185,188,190,193],{"class":45,"line":177},5,[43,179,181],{"class":180},"sP0c6","print",[43,183,184],{"class":108},"(df.to_string(",[43,186,187],{"class":155},"index",[43,189,144],{"class":104},[43,191,192],{"class":180},"False",[43,194,195],{"class":108},"))\n",[29,197,199],{"id":198},"step-1-correl-pearson-and-rsq","Step 1 — CORREL, PEARSON and RSQ",[10,201,202,205,206,209,210,213],{},[14,203,204],{},"CORREL(x, y)"," is the Pearson correlation coefficient, and so is ",[14,207,208],{},"PEARSON"," — they are the same function under two names. In pandas it is ",[14,211,212],{},"Series.corr",":",[34,215,217],{"className":95,"code":216,"language":97,"meta":39,"style":39},"r = df[\"TV\"].corr(df[\"Sales\"])\nprint(f\"CORREL(TV, Sales) = {r:.6f}\")\nprint(f\"RSQ(Sales, TV)    = {r ** 2:.6f}\")\nprint(df[[\"TV\", \"Online\", \"Sales\"]].corr().round(3))\n",[14,218,219,241,272,301],{"__ignoreMap":39},[43,220,221,224,226,229,232,235,238],{"class":45,"line":46},[43,222,223],{"class":108},"r ",[43,225,144],{"class":104},[43,227,228],{"class":108}," df[",[43,230,231],{"class":53},"\"TV\"",[43,233,234],{"class":108},"].corr(df[",[43,236,237],{"class":53},"\"Sales\"",[43,239,240],{"class":108},"])\n",[43,242,243,245,248,251,254,258,261,264,267,270],{"class":45,"line":118},[43,244,181],{"class":180},[43,246,247],{"class":108},"(",[43,249,250],{"class":104},"f",[43,252,253],{"class":53},"\"CORREL(TV, Sales) = ",[43,255,257],{"class":256},"sSjpA","{",[43,259,260],{"class":108},"r",[43,262,263],{"class":104},":.6f",[43,265,266],{"class":256},"}",[43,268,269],{"class":53},"\"",[43,271,174],{"class":108},[43,273,274,276,278,280,283,285,287,290,293,295,297,299],{"class":45,"line":131},[43,275,181],{"class":180},[43,277,247],{"class":108},[43,279,250],{"class":104},[43,281,282],{"class":53},"\"RSQ(Sales, TV)    = ",[43,284,257],{"class":256},[43,286,223],{"class":108},[43,288,289],{"class":104},"**",[43,291,292],{"class":180}," 2",[43,294,263],{"class":104},[43,296,266],{"class":256},[43,298,269],{"class":53},[43,300,174],{"class":108},[43,302,303,305,308,310,312,315,317,319,322,325],{"class":45,"line":138},[43,304,181],{"class":180},[43,306,307],{"class":108},"(df[[",[43,309,231],{"class":53},[43,311,79],{"class":108},[43,313,314],{"class":53},"\"Online\"",[43,316,79],{"class":108},[43,318,237],{"class":53},[43,320,321],{"class":108},"]].corr().round(",[43,323,324],{"class":180},"3",[43,326,195],{"class":108},[10,328,329,331,332,335,336,338,339,341,342,345],{},[14,330,82],{}," is simply the square of that correlation: the share of the variation in sales that a straight line on TV spend alone explains. ",[14,333,334],{},"DataFrame.corr()"," computes every pair at once — the correlation matrix that would take a grid of ",[14,337,16],{}," formulas in Excel. Missing values are handled differently: Excel's ",[14,340,16],{}," skips pairs where either cell is text or empty; pandas skips pairs where either value is ",[14,343,344],{},"NaN",", which amounts to the same thing once the columns are numeric.",[10,347,348,351],{},[14,349,350],{},"method=\"spearman\""," gives a rank correlation, which Excel has no single function for. It is the better measure when the relationship is consistent but not a straight line, or when one extreme month would otherwise dominate.",[353,354,362,363,362,367,362,371,362,379,362,387,362,394,362,401,362,406,362,412,362,414,362,419,362,422,362,425,362,427,362,431,362,434,362,438,362,440,362,444,362,447,362,451,362,453,362,457,362,460,362,463,362,465,362,469,362,472,362,476,362,478],"svg",{"viewBox":355,"role":356,"ariaLabelledBy":357,"xmlns":360,"style":361},"4 0 752 276","img",[358,359],"cl-map-t","cl-map-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[364,365,366],"title",{"id":358},"Excel statistics functions and their Python equivalents",[368,369,370],"desc",{"id":359},"A table mapping Excel functions to pandas, NumPy or SciPy calls. CORREL and PEARSON map to Series.corr, RSQ to the square of corr, SLOPE and INTERCEPT to scipy linregress, LINEST to numpy lstsq, and TREND to predictions from the fitted coefficients.",[372,373],"rect",{"x":374,"y":375,"width":376,"height":377,"fill":378},"4","0","752","276","#ffffff",[372,380],{"x":381,"y":382,"width":383,"height":384,"rx":375,"fill":378,"stroke":385,"style":386},"20.0","16.0","152.1","40.0","var(--line,#cdd5e6)","stroke-width:1px",[372,388],{"x":389,"y":382,"width":390,"height":384,"rx":375,"fill":391,"stroke":392,"style":393},"172.1","567.9","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:1.2px",[395,396,400],"text",{"x":397,"y":398,"style":399},"456.0","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","pandas \u002F NumPy \u002F SciPy",[372,402],{"x":381,"y":403,"width":383,"height":404,"rx":375,"fill":405,"stroke":385,"style":386},"56.0","34.0","#e7ebef",[395,407,411],{"x":408,"y":409,"style":410},"32.0","78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","CORREL, PEARSON",[372,413],{"x":389,"y":403,"width":390,"height":404,"rx":375,"fill":378,"stroke":385,"style":386},[395,415,418],{"x":397,"y":416,"style":417},"77.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","s1.corr(s2)",[372,420],{"x":381,"y":421,"width":383,"height":404,"rx":375,"fill":405,"stroke":385,"style":386},"90.0",[395,423,82],{"x":408,"y":424,"style":410},"112.0",[372,426],{"x":389,"y":421,"width":390,"height":404,"rx":375,"fill":378,"stroke":385,"style":386},[395,428,430],{"x":397,"y":429,"style":417},"111.0","s1.corr(s2) ** 2",[372,432],{"x":381,"y":433,"width":383,"height":404,"rx":375,"fill":405,"stroke":385,"style":386},"124.0",[395,435,437],{"x":408,"y":436,"style":410},"146.0","correlation grid",[372,439],{"x":389,"y":433,"width":390,"height":404,"rx":375,"fill":378,"stroke":385,"style":386},[395,441,443],{"x":397,"y":442,"style":417},"145.0","df.corr()",[372,445],{"x":381,"y":446,"width":383,"height":404,"rx":375,"fill":405,"stroke":385,"style":386},"158.0",[395,448,450],{"x":408,"y":449,"style":410},"180.0","SLOPE, INTERCEPT",[372,452],{"x":389,"y":446,"width":390,"height":404,"rx":375,"fill":378,"stroke":385,"style":386},[395,454,456],{"x":397,"y":455,"style":417},"179.0","scipy.stats.linregress(x, y)",[372,458],{"x":381,"y":459,"width":383,"height":404,"rx":375,"fill":405,"stroke":385,"style":386},"192.0",[395,461,20],{"x":408,"y":462,"style":410},"214.0",[372,464],{"x":389,"y":459,"width":390,"height":404,"rx":375,"fill":378,"stroke":385,"style":386},[395,466,468],{"x":397,"y":467,"style":417},"213.0","np.linalg.lstsq(X, y)",[372,470],{"x":381,"y":471,"width":383,"height":404,"rx":375,"fill":405,"stroke":385,"style":386},"226.0",[395,473,475],{"x":408,"y":474,"style":410},"248.0","TREND",[372,477],{"x":389,"y":471,"width":390,"height":404,"rx":375,"fill":378,"stroke":385,"style":386},[395,479,481],{"x":397,"y":480,"style":417},"247.0","X_new @ coefficients",[29,483,485],{"id":484},"step-2-simple-regression-slope-intercept-and-friends","Step 2 — Simple regression: SLOPE, INTERCEPT and friends",[10,487,488,489,492,493,79,496,79,499,501,502,504],{},"For one input, ",[14,490,491],{},"scipy.stats.linregress"," returns the slope, intercept, correlation and the standard error of the slope in one call — the numbers ",[14,494,495],{},"SLOPE",[14,497,498],{},"INTERCEPT",[14,500,16],{}," and the second row of a one-variable ",[14,503,20],{}," give:",[34,506,508],{"className":95,"code":507,"language":97,"meta":39,"style":39},"from scipy.stats import linregress\n\nfit = linregress(df[\"TV\"], df[\"Sales\"])\nprint(f\"slope {fit.slope:.4f}  intercept {fit.intercept:.4f}  r {fit.rvalue:.4f}  se {fit.stderr:.4f}\")\n",[14,509,510,523,527,546],{"__ignoreMap":39},[43,511,512,515,518,520],{"class":45,"line":46},[43,513,514],{"class":104},"from",[43,516,517],{"class":108}," scipy.stats ",[43,519,105],{"class":104},[43,521,522],{"class":108}," linregress\n",[43,524,525],{"class":45,"line":118},[43,526,135],{"emptyLinePlaceholder":134},[43,528,529,532,534,537,539,542,544],{"class":45,"line":131},[43,530,531],{"class":108},"fit ",[43,533,144],{"class":104},[43,535,536],{"class":108}," linregress(df[",[43,538,231],{"class":53},[43,540,541],{"class":108},"], df[",[43,543,237],{"class":53},[43,545,240],{"class":108},[43,547,548,550,552,554,557,559,562,565,567,570,572,575,577,579,582,584,587,589,591,594,596,599,601,603,605],{"class":45,"line":138},[43,549,181],{"class":180},[43,551,247],{"class":108},[43,553,250],{"class":104},[43,555,556],{"class":53},"\"slope ",[43,558,257],{"class":256},[43,560,561],{"class":108},"fit.slope",[43,563,564],{"class":104},":.4f",[43,566,266],{"class":256},[43,568,569],{"class":53},"  intercept ",[43,571,257],{"class":256},[43,573,574],{"class":108},"fit.intercept",[43,576,564],{"class":104},[43,578,266],{"class":256},[43,580,581],{"class":53},"  r ",[43,583,257],{"class":256},[43,585,586],{"class":108},"fit.rvalue",[43,588,564],{"class":104},[43,590,266],{"class":256},[43,592,593],{"class":53},"  se ",[43,595,257],{"class":256},[43,597,598],{"class":108},"fit.stderr",[43,600,564],{"class":104},[43,602,266],{"class":256},[43,604,269],{"class":53},[43,606,174],{"class":108},[10,608,609,610,614],{},"With TV spend alone, each extra thousand spent goes with about 1.87 thousand more in sales. That number turns out to understate TV's effect, which is the reason for the next step. The correlation matrix above showed TV and online spend moving slightly ",[611,612,613],"em",{},"against"," each other (−0.23): in months with heavy TV spend, online spend tended to be lower. A one-variable fit cannot separate the two, so part of the online effect shows up as a smaller TV slope. Only a fit that includes both inputs can attribute sales to each.",[29,616,618],{"id":617},"step-3-multiple-regression-linest-with-several-inputs","Step 3 — Multiple regression: LINEST with several inputs",[10,620,621,624],{},[14,622,623],{},"LINEST(y, x1:x2)"," fits sales on both spends at once. In NumPy, add a column of ones for the intercept and solve with least squares:",[34,626,628],{"className":95,"code":627,"language":97,"meta":39,"style":39},"X = np.column_stack([np.ones(len(df)), df[\"TV\"], df[\"Online\"]])\ny = df[\"Sales\"].to_numpy()\ncoef, *_ = np.linalg.lstsq(X, y, rcond=None)\nprint(dict(zip([\"intercept\", \"TV\", \"Online\"], coef.round(4).tolist())))\n",[14,629,630,655,669,695],{"__ignoreMap":39},[43,631,632,635,637,640,643,646,648,650,652],{"class":45,"line":46},[43,633,634],{"class":108},"X ",[43,636,144],{"class":104},[43,638,639],{"class":108}," np.column_stack([np.ones(",[43,641,642],{"class":180},"len",[43,644,645],{"class":108},"(df)), df[",[43,647,231],{"class":53},[43,649,541],{"class":108},[43,651,314],{"class":53},[43,653,654],{"class":108},"]])\n",[43,656,657,660,662,664,666],{"class":45,"line":118},[43,658,659],{"class":108},"y ",[43,661,144],{"class":104},[43,663,228],{"class":108},[43,665,237],{"class":53},[43,667,668],{"class":108},"].to_numpy()\n",[43,670,671,674,677,680,682,685,688,690,693],{"class":45,"line":131},[43,672,673],{"class":108},"coef, ",[43,675,676],{"class":104},"*",[43,678,679],{"class":108},"_ ",[43,681,144],{"class":104},[43,683,684],{"class":108}," np.linalg.lstsq(X, y, ",[43,686,687],{"class":155},"rcond",[43,689,144],{"class":104},[43,691,692],{"class":180},"None",[43,694,174],{"class":108},[43,696,697,699,701,704,706,709,712,715,717,719,721,723,726,728],{"class":45,"line":138},[43,698,181],{"class":180},[43,700,247],{"class":108},[43,702,703],{"class":180},"dict",[43,705,247],{"class":108},[43,707,708],{"class":180},"zip",[43,710,711],{"class":108},"([",[43,713,714],{"class":53},"\"intercept\"",[43,716,79],{"class":108},[43,718,231],{"class":53},[43,720,79],{"class":108},[43,722,314],{"class":53},[43,724,725],{"class":108},"], coef.round(",[43,727,374],{"class":180},[43,729,730],{"class":108},").tolist())))\n",[10,732,733],{},"Now TV is worth about 2.18 per thousand and online about 3.26, with a baseline of about 38.4 — close to the values the data was generated from, and a noticeably larger TV effect than the one-variable fit suggested.",[353,735,362,741,362,744,362,747,362,751,362,756,362,762,362,767,362,772,362,774,362,778,362,781,362,786,362,789,362,793,362,798,362,803],{"viewBox":736,"role":356,"ariaLabelledBy":737,"xmlns":360,"style":740},"4 0 744 166",[738,739],"cl-coef-t","cl-coef-d","width:100%;max-width:744px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[364,742,743],{"id":738},"TV's effect on sales depends on what else is in the model",[368,745,746],{"id":739},"Horizontal bars comparing coefficients. With TV alone the slope is 1.87; with TV and online spend together, TV's coefficient is 2.18 and online's is 3.26.",[372,748],{"x":374,"y":375,"width":749,"height":750,"fill":378},"744","166",[372,752],{"x":381,"y":382,"width":753,"height":754,"rx":755,"fill":405,"stroke":385,"style":386},"138.1","28.0","7",[395,757,761],{"x":758,"y":759,"style":760},"89.1","34.5","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","TV — alone",[372,763],{"x":764,"y":382,"width":765,"height":754,"rx":755,"fill":405,"stroke":385,"style":766},"168.1","302.5","stroke-width:1.5px",[395,768,771],{"x":769,"y":759,"style":770},"478.7","font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:start","1.87",[372,773],{"x":381,"y":403,"width":753,"height":754,"rx":755,"fill":405,"stroke":385,"style":386},[395,775,777],{"x":758,"y":776,"style":760},"74.5","TV — with online",[372,779],{"x":764,"y":403,"width":780,"height":754,"rx":755,"fill":391,"stroke":392,"style":766},"352.7",[395,782,785],{"x":783,"y":776,"style":784},"528.8","font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:start","2.18",[372,787],{"x":381,"y":788,"width":753,"height":754,"rx":755,"fill":405,"stroke":385,"style":386},"96.0",[395,790,792],{"x":758,"y":791,"style":760},"114.5","Online — with TV",[372,794],{"x":764,"y":788,"width":795,"height":754,"rx":755,"fill":796,"stroke":797,"style":766},"527.4","#d9f4f1","var(--teal,#0f9488)",[395,799,802],{"x":800,"y":791,"style":801},"703.5","font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:start","3.26",[395,804,807],{"x":805,"y":436,"style":806},"380.0","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","Sales in thousands per thousand spent, from the same twelve months.",[10,809,810,811,75,814,816,817,820],{},"The column of ones is what ",[14,812,813],{},"const=TRUE",[14,815,20],{}," means; leave it out to force the line through zero, as ",[14,818,819],{},"const=FALSE"," does.",[29,822,824],{"id":823},"step-4-the-full-linest-statistics-block","Step 4 — The full LINEST statistics block",[10,826,827,830,831,835],{},[14,828,829],{},"LINEST(..., TRUE, TRUE)"," returns five rows: coefficients, their standard errors, R² and the standard error of the estimate, the F statistic and degrees of freedom, and the regression and residual sums of squares. It also returns the coefficients in ",[832,833,834],"strong",{},"reverse"," order of the input columns, with the intercept last. A function that reproduces the whole block, in Excel's layout:",[34,837,839],{"className":95,"code":838,"language":97,"meta":39,"style":39},"def linest(y, X_cols, const=True):\n    Xm = np.column_stack(X_cols)\n    if const:\n        Xm = np.column_stack([Xm, np.ones(len(y))])\n    n, k = Xm.shape\n    coef, *_ = np.linalg.lstsq(Xm, y, rcond=None)\n    resid = y - Xm @ coef\n    ss_resid = float(resid @ resid)\n    ss_total = float(((y - y.mean()) ** 2).sum()) if const else float(y @ y)\n    ss_reg = ss_total - ss_resid\n    df_resid = n - k\n    se_y = (ss_resid \u002F df_resid) ** 0.5\n    se = np.sqrt(np.diag(np.linalg.inv(Xm.T @ Xm)) * se_y ** 2)\n    k_x = k - 1 if const else k\n    f = (ss_reg \u002F k_x) \u002F (ss_resid \u002F df_resid)\n    order = list(range(len(X_cols) - 1, -1, -1)) + ([len(X_cols)] if const else [])\n    na = [np.nan] * (len(order) - 2)\n    return pd.DataFrame([coef[order], se[order], [ss_reg \u002F ss_total, se_y] + na,\n                         [f, df_resid] + na, [ss_reg, ss_resid] + na])\n\nblock = linest(y, [df[\"TV\"].to_numpy(), df[\"Online\"].to_numpy()])\nprint(block.round(6).to_string(header=False, index=False))\n",[14,840,841,861,871,879,894,904,927,950,969,1013,1029,1045,1067,1094,1119,1144,1207,1234,1253,1269,1274,1295],{"__ignoreMap":39},[43,842,843,846,850,853,855,858],{"class":45,"line":46},[43,844,845],{"class":104},"def",[43,847,849],{"class":848},"s_Opv"," linest",[43,851,852],{"class":108},"(y, X_cols, const",[43,854,144],{"class":104},[43,856,857],{"class":180},"True",[43,859,860],{"class":108},"):\n",[43,862,863,866,868],{"class":45,"line":118},[43,864,865],{"class":108},"    Xm ",[43,867,144],{"class":104},[43,869,870],{"class":108}," np.column_stack(X_cols)\n",[43,872,873,876],{"class":45,"line":131},[43,874,875],{"class":104},"    if",[43,877,878],{"class":108}," const:\n",[43,880,881,884,886,889,891],{"class":45,"line":138},[43,882,883],{"class":108},"        Xm ",[43,885,144],{"class":104},[43,887,888],{"class":108}," np.column_stack([Xm, np.ones(",[43,890,642],{"class":180},[43,892,893],{"class":108},"(y))])\n",[43,895,896,899,901],{"class":45,"line":177},[43,897,898],{"class":108},"    n, k ",[43,900,144],{"class":104},[43,902,903],{"class":108}," Xm.shape\n",[43,905,907,910,912,914,916,919,921,923,925],{"class":45,"line":906},6,[43,908,909],{"class":108},"    coef, ",[43,911,676],{"class":104},[43,913,679],{"class":108},[43,915,144],{"class":104},[43,917,918],{"class":108}," np.linalg.lstsq(Xm, y, ",[43,920,687],{"class":155},[43,922,144],{"class":104},[43,924,692],{"class":180},[43,926,174],{"class":108},[43,928,930,933,935,938,941,944,947],{"class":45,"line":929},7,[43,931,932],{"class":108},"    resid ",[43,934,144],{"class":104},[43,936,937],{"class":108}," y ",[43,939,940],{"class":104},"-",[43,942,943],{"class":108}," Xm ",[43,945,946],{"class":104},"@",[43,948,949],{"class":108}," coef\n",[43,951,953,956,958,961,964,966],{"class":45,"line":952},8,[43,954,955],{"class":108},"    ss_resid ",[43,957,144],{"class":104},[43,959,960],{"class":180}," float",[43,962,963],{"class":108},"(resid ",[43,965,946],{"class":104},[43,967,968],{"class":108}," resid)\n",[43,970,972,975,977,979,982,984,987,989,991,994,997,1000,1003,1005,1008,1010],{"class":45,"line":971},9,[43,973,974],{"class":108},"    ss_total ",[43,976,144],{"class":104},[43,978,960],{"class":180},[43,980,981],{"class":108},"(((y ",[43,983,940],{"class":104},[43,985,986],{"class":108}," y.mean()) ",[43,988,289],{"class":104},[43,990,292],{"class":180},[43,992,993],{"class":108},").sum()) ",[43,995,996],{"class":104},"if",[43,998,999],{"class":108}," const ",[43,1001,1002],{"class":104},"else",[43,1004,960],{"class":180},[43,1006,1007],{"class":108},"(y ",[43,1009,946],{"class":104},[43,1011,1012],{"class":108}," y)\n",[43,1014,1016,1019,1021,1024,1026],{"class":45,"line":1015},10,[43,1017,1018],{"class":108},"    ss_reg ",[43,1020,144],{"class":104},[43,1022,1023],{"class":108}," ss_total ",[43,1025,940],{"class":104},[43,1027,1028],{"class":108}," ss_resid\n",[43,1030,1032,1035,1037,1040,1042],{"class":45,"line":1031},11,[43,1033,1034],{"class":108},"    df_resid ",[43,1036,144],{"class":104},[43,1038,1039],{"class":108}," n ",[43,1041,940],{"class":104},[43,1043,1044],{"class":108}," k\n",[43,1046,1048,1051,1053,1056,1059,1062,1064],{"class":45,"line":1047},12,[43,1049,1050],{"class":108},"    se_y ",[43,1052,144],{"class":104},[43,1054,1055],{"class":108}," (ss_resid ",[43,1057,1058],{"class":104},"\u002F",[43,1060,1061],{"class":108}," df_resid) ",[43,1063,289],{"class":104},[43,1065,1066],{"class":180}," 0.5\n",[43,1068,1070,1073,1075,1078,1080,1083,1085,1088,1090,1092],{"class":45,"line":1069},13,[43,1071,1072],{"class":108},"    se ",[43,1074,144],{"class":104},[43,1076,1077],{"class":108}," np.sqrt(np.diag(np.linalg.inv(Xm.T ",[43,1079,946],{"class":104},[43,1081,1082],{"class":108}," Xm)) ",[43,1084,676],{"class":104},[43,1086,1087],{"class":108}," se_y ",[43,1089,289],{"class":104},[43,1091,292],{"class":180},[43,1093,174],{"class":108},[43,1095,1097,1100,1102,1105,1107,1110,1113,1115,1117],{"class":45,"line":1096},14,[43,1098,1099],{"class":108},"    k_x ",[43,1101,144],{"class":104},[43,1103,1104],{"class":108}," k ",[43,1106,940],{"class":104},[43,1108,1109],{"class":180}," 1",[43,1111,1112],{"class":104}," if",[43,1114,999],{"class":108},[43,1116,1002],{"class":104},[43,1118,1044],{"class":108},[43,1120,1122,1125,1127,1130,1132,1135,1137,1139,1141],{"class":45,"line":1121},15,[43,1123,1124],{"class":108},"    f ",[43,1126,144],{"class":104},[43,1128,1129],{"class":108}," (ss_reg ",[43,1131,1058],{"class":104},[43,1133,1134],{"class":108}," k_x) ",[43,1136,1058],{"class":104},[43,1138,1055],{"class":108},[43,1140,1058],{"class":104},[43,1142,1143],{"class":108}," df_resid)\n",[43,1145,1147,1150,1152,1155,1157,1160,1162,1164,1167,1169,1171,1173,1175,1178,1180,1182,1184,1187,1190,1193,1195,1198,1200,1202,1204],{"class":45,"line":1146},16,[43,1148,1149],{"class":108},"    order ",[43,1151,144],{"class":104},[43,1153,1154],{"class":180}," list",[43,1156,247],{"class":108},[43,1158,1159],{"class":180},"range",[43,1161,247],{"class":108},[43,1163,642],{"class":180},[43,1165,1166],{"class":108},"(X_cols) ",[43,1168,940],{"class":104},[43,1170,1109],{"class":180},[43,1172,79],{"class":108},[43,1174,940],{"class":104},[43,1176,1177],{"class":180},"1",[43,1179,79],{"class":108},[43,1181,940],{"class":104},[43,1183,1177],{"class":180},[43,1185,1186],{"class":108},")) ",[43,1188,1189],{"class":104},"+",[43,1191,1192],{"class":108}," ([",[43,1194,642],{"class":180},[43,1196,1197],{"class":108},"(X_cols)] ",[43,1199,996],{"class":104},[43,1201,999],{"class":108},[43,1203,1002],{"class":104},[43,1205,1206],{"class":108}," [])\n",[43,1208,1210,1213,1215,1218,1220,1223,1225,1228,1230,1232],{"class":45,"line":1209},17,[43,1211,1212],{"class":108},"    na ",[43,1214,144],{"class":104},[43,1216,1217],{"class":108}," [np.nan] ",[43,1219,676],{"class":104},[43,1221,1222],{"class":108}," (",[43,1224,642],{"class":180},[43,1226,1227],{"class":108},"(order) ",[43,1229,940],{"class":104},[43,1231,292],{"class":180},[43,1233,174],{"class":108},[43,1235,1237,1240,1243,1245,1248,1250],{"class":45,"line":1236},18,[43,1238,1239],{"class":104},"    return",[43,1241,1242],{"class":108}," pd.DataFrame([coef[order], se[order], [ss_reg ",[43,1244,1058],{"class":104},[43,1246,1247],{"class":108}," ss_total, se_y] ",[43,1249,1189],{"class":104},[43,1251,1252],{"class":108}," na,\n",[43,1254,1256,1259,1261,1264,1266],{"class":45,"line":1255},19,[43,1257,1258],{"class":108},"                         [f, df_resid] ",[43,1260,1189],{"class":104},[43,1262,1263],{"class":108}," na, [ss_reg, ss_resid] ",[43,1265,1189],{"class":104},[43,1267,1268],{"class":108}," na])\n",[43,1270,1272],{"class":45,"line":1271},20,[43,1273,135],{"emptyLinePlaceholder":134},[43,1275,1277,1280,1282,1285,1287,1290,1292],{"class":45,"line":1276},21,[43,1278,1279],{"class":108},"block ",[43,1281,144],{"class":104},[43,1283,1284],{"class":108}," linest(y, [df[",[43,1286,231],{"class":53},[43,1288,1289],{"class":108},"].to_numpy(), df[",[43,1291,314],{"class":53},[43,1293,1294],{"class":108},"].to_numpy()])\n",[43,1296,1298,1300,1303,1306,1309,1312,1314,1316,1318,1320,1322,1324],{"class":45,"line":1297},22,[43,1299,181],{"class":180},[43,1301,1302],{"class":108},"(block.round(",[43,1304,1305],{"class":180},"6",[43,1307,1308],{"class":108},").to_string(",[43,1310,1311],{"class":155},"header",[43,1313,144],{"class":104},[43,1315,192],{"class":180},[43,1317,79],{"class":108},[43,1319,187],{"class":155},[43,1321,144],{"class":104},[43,1323,192],{"class":180},[43,1325,195],{"class":108},[10,1327,1328,1329,1332],{},"The first row reads Online, TV, intercept — the reverse of the ",[14,1330,1331],{},"B:C"," column order in the formula. That reversal is the most common reason a script that \"reads LINEST from the sheet\" attaches the wrong coefficient to the wrong input. Reading the block row by row: R² of about 0.968 means the two spends explain almost all of the month-to-month variation in sales; the F statistic of about 136 on 9 residual degrees of freedom says the fit is far from chance.",[353,1334,362,1340,362,1343,362,1346,362,1351,362,1355,362,1357,362,1363,362,1368,362,1374,362,1380,362,1384,362,1388,362,1392,362,1397,362,1400,362,1405,362,1407,362,1410,362,1412,362,1415,362,1418,362,1422,362,1424,362,1427,362,1429,362,1432,362,1435,362,1439,362,1441,362,1444,362,1446,362,1448,362,1451,362,1455,362,1457,362,1460,362,1462],{"viewBox":1335,"role":356,"ariaLabelledBy":1336,"xmlns":360,"style":1339},"191 0 378 200",[1337,1338],"cl-block-t","cl-block-d","width:100%;max-width:378px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[364,1341,1342],{"id":1337},"How LINEST lays out its results",[368,1344,1345],{"id":1338},"A five-row by three-column grid showing the LINEST output for two inputs. The first row holds the Online coefficient, the TV coefficient and the intercept — reversed from the input order. Below are standard errors, R squared and the standard error of y, F and degrees of freedom, and the sums of squares.",[372,1347],{"x":1348,"y":375,"width":1349,"height":1350,"fill":378},"191","378","200",[372,1352],{"x":1353,"y":382,"width":1354,"height":754,"rx":375,"fill":378,"stroke":385,"style":386},"207.1","345.7",[372,1356],{"x":1353,"y":382,"width":384,"height":754,"rx":375,"fill":405,"stroke":385,"style":386},[395,1358,1362],{"x":1359,"y":1360,"style":1361},"227.1","35.0","font-size:12px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","fx",[395,1364,1367],{"x":1365,"y":1360,"style":1366},"257.1","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace","=LINEST(D2:D13,B2:C13,TRUE,TRUE)",[372,1369],{"x":1353,"y":1370,"width":1371,"height":754,"rx":375,"fill":1372,"stroke":1373,"style":766},"44.0","129.6","#fdefd8","var(--gold,#b4740a)",[395,1375,1379],{"x":1376,"y":1377,"style":1378},"271.9","63.0","font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","m Online: 3.2556",[372,1381],{"x":1382,"y":1370,"width":1383,"height":754,"rx":375,"fill":1372,"stroke":1373,"style":766},"336.7","122.8",[395,1385,1387],{"x":1386,"y":1377,"style":1378},"398.1","m TV: 2.1760",[372,1389],{"x":1390,"y":1370,"width":1391,"height":754,"rx":375,"fill":391,"stroke":392,"style":766},"459.5","93.4",[395,1393,1396],{"x":1394,"y":1377,"style":1395},"506.2","font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","b: 38.4446",[372,1398],{"x":1353,"y":1399,"width":1371,"height":754,"rx":375,"fill":378,"stroke":385,"style":386},"72.0",[395,1401,1404],{"x":1376,"y":1402,"style":1403},"91.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","se: 0.3471",[372,1406],{"x":1382,"y":1399,"width":1383,"height":754,"rx":375,"fill":378,"stroke":385,"style":386},[395,1408,1409],{"x":1386,"y":1402,"style":1403},"se: 0.1418",[372,1411],{"x":1390,"y":1399,"width":1391,"height":754,"rx":375,"fill":378,"stroke":385,"style":386},[395,1413,1414],{"x":1394,"y":1402,"style":1403},"se: 4.3889",[372,1416],{"x":1353,"y":1417,"width":1371,"height":754,"rx":375,"fill":378,"stroke":385,"style":386},"100.0",[395,1419,1421],{"x":1376,"y":1420,"style":1403},"119.0","R²: 0.9679",[372,1423],{"x":1382,"y":1417,"width":1383,"height":754,"rx":375,"fill":378,"stroke":385,"style":386},[395,1425,1426],{"x":1386,"y":1420,"style":1403},"se y: 3.6937",[372,1428],{"x":1390,"y":1417,"width":1391,"height":754,"rx":375,"fill":378,"stroke":385,"style":386},[395,1430,1431],{"x":1394,"y":1420,"style":1403},"#N\u002FA",[372,1433],{"x":1353,"y":1434,"width":1371,"height":754,"rx":375,"fill":378,"stroke":385,"style":386},"128.0",[395,1436,1438],{"x":1376,"y":1437,"style":1403},"147.0","F: 135.83",[372,1440],{"x":1382,"y":1434,"width":1383,"height":754,"rx":375,"fill":378,"stroke":385,"style":386},[395,1442,1443],{"x":1386,"y":1437,"style":1403},"df: 9",[372,1445],{"x":1390,"y":1434,"width":1391,"height":754,"rx":375,"fill":378,"stroke":385,"style":386},[395,1447,1431],{"x":1394,"y":1437,"style":1403},[372,1449],{"x":1353,"y":1450,"width":1371,"height":754,"rx":375,"fill":378,"stroke":385,"style":386},"156.0",[395,1452,1454],{"x":1376,"y":1453,"style":1403},"175.0","SS reg: 3706.25",[372,1456],{"x":1382,"y":1450,"width":1383,"height":754,"rx":375,"fill":378,"stroke":385,"style":386},[395,1458,1459],{"x":1386,"y":1453,"style":1403},"SS resid: 122.79",[372,1461],{"x":1390,"y":1450,"width":1391,"height":754,"rx":375,"fill":378,"stroke":385,"style":386},[395,1463,1431],{"x":1394,"y":1453,"style":1403},[29,1465,1467],{"id":1466},"step-5-check-against-the-spreadsheet","Step 5 — Check against the spreadsheet",[10,1469,1470],{},"The workbook contains the same calculations as formulas. Recalculate a copy with headless LibreOffice and compare the two results cell by cell:",[34,1472,1474],{"className":36,"code":1473,"language":38,"meta":39,"style":39},"soffice -env:UserInstallation=file:\u002F\u002F$PWD\u002Flo_profile --headless --convert-to xlsx --outdir recalculated ads.xlsx > \u002Fdev\u002Fnull 2>&1\nls recalculated\n",[14,1475,1476,1517],{"__ignoreMap":39},[43,1477,1478,1481,1484,1487,1490,1493,1496,1499,1502,1505,1508,1511,1514],{"class":45,"line":46},[43,1479,1480],{"class":49},"soffice",[43,1482,1483],{"class":180}," -env:UserInstallation=file:\u002F\u002F",[43,1485,1486],{"class":108},"$PWD",[43,1488,1489],{"class":180},"\u002Flo_profile",[43,1491,1492],{"class":180}," --headless",[43,1494,1495],{"class":180}," --convert-to",[43,1497,1498],{"class":53}," xlsx",[43,1500,1501],{"class":180}," --outdir",[43,1503,1504],{"class":53}," recalculated",[43,1506,1507],{"class":53}," ads.xlsx",[43,1509,1510],{"class":104}," >",[43,1512,1513],{"class":53}," \u002Fdev\u002Fnull",[43,1515,1516],{"class":104}," 2>&1\n",[43,1518,1519,1522],{"class":45,"line":118},[43,1520,1521],{"class":49},"ls",[43,1523,1524],{"class":53}," recalculated\n",[34,1526,1528],{"className":95,"code":1527,"language":97,"meta":39,"style":39},"from openpyxl import load_workbook\n\nws = load_workbook(\"recalculated\u002Fads.xlsx\", data_only=True)[\"Ads\"]\nsheet_block = pd.DataFrame([[c if isinstance(c, (int, float)) else np.nan for c in row]\n                            for row in ws.iter_rows(min_row=4, max_row=8, min_col=6, max_col=8, values_only=True)])\nprint(\"CORREL matches:\", np.isclose(ws[\"F1\"].value, r), \" RSQ matches:\", np.isclose(ws[\"F2\"].value, r ** 2))\nprint(\"LINEST block matches:\", np.allclose(sheet_block, block, equal_nan=True))\n",[14,1529,1530,1542,1546,1576,1621,1681,1716],{"__ignoreMap":39},[43,1531,1532,1534,1537,1539],{"class":45,"line":46},[43,1533,514],{"class":104},[43,1535,1536],{"class":108}," openpyxl ",[43,1538,105],{"class":104},[43,1540,1541],{"class":108}," load_workbook\n",[43,1543,1544],{"class":45,"line":118},[43,1545,135],{"emptyLinePlaceholder":134},[43,1547,1548,1551,1553,1556,1559,1561,1564,1566,1568,1571,1573],{"class":45,"line":131},[43,1549,1550],{"class":108},"ws ",[43,1552,144],{"class":104},[43,1554,1555],{"class":108}," load_workbook(",[43,1557,1558],{"class":53},"\"recalculated\u002Fads.xlsx\"",[43,1560,79],{"class":108},[43,1562,1563],{"class":155},"data_only",[43,1565,144],{"class":104},[43,1567,857],{"class":180},[43,1569,1570],{"class":108},")[",[43,1572,161],{"class":53},[43,1574,1575],{"class":108},"]\n",[43,1577,1578,1581,1583,1586,1588,1591,1594,1597,1599,1602,1604,1606,1609,1612,1615,1618],{"class":45,"line":138},[43,1579,1580],{"class":108},"sheet_block ",[43,1582,144],{"class":104},[43,1584,1585],{"class":108}," pd.DataFrame([[c ",[43,1587,996],{"class":104},[43,1589,1590],{"class":180}," isinstance",[43,1592,1593],{"class":108},"(c, (",[43,1595,1596],{"class":180},"int",[43,1598,79],{"class":108},[43,1600,1601],{"class":180},"float",[43,1603,1186],{"class":108},[43,1605,1002],{"class":104},[43,1607,1608],{"class":108}," np.nan ",[43,1610,1611],{"class":104},"for",[43,1613,1614],{"class":108}," c ",[43,1616,1617],{"class":104},"in",[43,1619,1620],{"class":108}," row]\n",[43,1622,1623,1626,1629,1631,1634,1637,1639,1641,1643,1646,1648,1651,1653,1656,1658,1660,1662,1665,1667,1669,1671,1674,1676,1678],{"class":45,"line":177},[43,1624,1625],{"class":104},"                            for",[43,1627,1628],{"class":108}," row ",[43,1630,1617],{"class":104},[43,1632,1633],{"class":108}," ws.iter_rows(",[43,1635,1636],{"class":155},"min_row",[43,1638,144],{"class":104},[43,1640,374],{"class":180},[43,1642,79],{"class":108},[43,1644,1645],{"class":155},"max_row",[43,1647,144],{"class":104},[43,1649,1650],{"class":180},"8",[43,1652,79],{"class":108},[43,1654,1655],{"class":155},"min_col",[43,1657,144],{"class":104},[43,1659,1305],{"class":180},[43,1661,79],{"class":108},[43,1663,1664],{"class":155},"max_col",[43,1666,144],{"class":104},[43,1668,1650],{"class":180},[43,1670,79],{"class":108},[43,1672,1673],{"class":155},"values_only",[43,1675,144],{"class":104},[43,1677,857],{"class":180},[43,1679,1680],{"class":108},")])\n",[43,1682,1683,1685,1687,1690,1693,1696,1699,1702,1704,1707,1710,1712,1714],{"class":45,"line":906},[43,1684,181],{"class":180},[43,1686,247],{"class":108},[43,1688,1689],{"class":53},"\"CORREL matches:\"",[43,1691,1692],{"class":108},", np.isclose(ws[",[43,1694,1695],{"class":53},"\"F1\"",[43,1697,1698],{"class":108},"].value, r), ",[43,1700,1701],{"class":53},"\" RSQ matches:\"",[43,1703,1692],{"class":108},[43,1705,1706],{"class":53},"\"F2\"",[43,1708,1709],{"class":108},"].value, r ",[43,1711,289],{"class":104},[43,1713,292],{"class":180},[43,1715,195],{"class":108},[43,1717,1718,1720,1722,1725,1728,1731,1733,1735],{"class":45,"line":929},[43,1719,181],{"class":180},[43,1721,247],{"class":108},[43,1723,1724],{"class":53},"\"LINEST block matches:\"",[43,1726,1727],{"class":108},", np.allclose(sheet_block, block, ",[43,1729,1730],{"class":155},"equal_nan",[43,1732,144],{"class":104},[43,1734,857],{"class":180},[43,1736,195],{"class":108},[10,1738,1739,1740,1742,1743,1747],{},"Every value agrees to floating-point precision, including the ",[14,1741,1431],{}," padding, which the comparison treats as missing. A check like this is worth keeping in a test when a report switches from spreadsheet formulas to Python: it proves the replacement computes the same thing before anyone starts relying on it. ",[23,1744,1746],{"href":1745},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Frecalculate-excel-formulas-without-excel-in-python\u002F","Recalculate Excel Formulas Without Excel"," covers the LibreOffice step.",[29,1749,1751],{"id":1750},"step-6-predict-the-trend-equivalent","Step 6 — Predict: the TREND equivalent",[10,1753,1754,1756],{},[14,1755,475],{}," applies the fitted coefficients to new inputs. With the coefficients in natural order from Step 3, it is a matrix product:",[34,1758,1760],{"className":95,"code":1759,"language":97,"meta":39,"style":39},"plan = pd.DataFrame({\"TV\": [15.0, 25.0], \"Online\": [10.0, 6.0]})\nX_new = np.column_stack([np.ones(len(plan)), plan[\"TV\"], plan[\"Online\"]])\nplan[\"Predicted sales\"] = (X_new @ coef).round(1)\nprint(plan.to_string(index=False))\n",[14,1761,1762,1803,1826,1851],{"__ignoreMap":39},[43,1763,1764,1767,1769,1772,1774,1777,1780,1782,1785,1788,1790,1792,1795,1797,1800],{"class":45,"line":46},[43,1765,1766],{"class":108},"plan ",[43,1768,144],{"class":104},[43,1770,1771],{"class":108}," pd.DataFrame({",[43,1773,231],{"class":53},[43,1775,1776],{"class":108},": [",[43,1778,1779],{"class":180},"15.0",[43,1781,79],{"class":108},[43,1783,1784],{"class":180},"25.0",[43,1786,1787],{"class":108},"], ",[43,1789,314],{"class":53},[43,1791,1776],{"class":108},[43,1793,1794],{"class":180},"10.0",[43,1796,79],{"class":108},[43,1798,1799],{"class":180},"6.0",[43,1801,1802],{"class":108},"]})\n",[43,1804,1805,1808,1810,1812,1814,1817,1819,1822,1824],{"class":45,"line":118},[43,1806,1807],{"class":108},"X_new ",[43,1809,144],{"class":104},[43,1811,639],{"class":108},[43,1813,642],{"class":180},[43,1815,1816],{"class":108},"(plan)), plan[",[43,1818,231],{"class":53},[43,1820,1821],{"class":108},"], plan[",[43,1823,314],{"class":53},[43,1825,654],{"class":108},[43,1827,1828,1831,1834,1837,1839,1842,1844,1847,1849],{"class":45,"line":131},[43,1829,1830],{"class":108},"plan[",[43,1832,1833],{"class":53},"\"Predicted sales\"",[43,1835,1836],{"class":108},"] ",[43,1838,144],{"class":104},[43,1840,1841],{"class":108}," (X_new ",[43,1843,946],{"class":104},[43,1845,1846],{"class":108}," coef).round(",[43,1848,1177],{"class":180},[43,1850,174],{"class":108},[43,1852,1853,1855,1858,1860,1862,1864],{"class":45,"line":138},[43,1854,181],{"class":180},[43,1856,1857],{"class":108},"(plan.to_string(",[43,1859,187],{"class":155},[43,1861,144],{"class":104},[43,1863,192],{"class":180},[43,1865,195],{"class":108},[10,1867,1868,1872],{},[23,1869,1871],{"href":1870},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fforecast-trend-and-slope-equivalents-in-python\u002F","FORECAST, TREND and SLOPE Equivalents in Python"," covers forecasting over time rather than from other inputs.",[29,1874,1876],{"id":1875},"performance-notes","Performance notes",[10,1878,1879,1880,1883,1884,1887,1888,1891],{},"All of this is instant for reporting-sized data; ",[14,1881,1882],{},"lstsq"," handles hundreds of thousands of rows and dozens of inputs in well under a second. For more than the LINEST statistics — p-values, confidence intervals, robust standard errors, categorical inputs — use statsmodels, whose ",[14,1885,1886],{},"OLS(...).fit().summary()"," prints a complete regression table. The ",[14,1889,1890],{},"linest()"," function here earns its place when the goal is to match an existing spreadsheet number for number.",[29,1893,1895],{"id":1894},"common-pitfalls","Common pitfalls",[1897,1898,1899,1910,1921,1927,1933],"ul",{},[1900,1901,1902,1905,1906,1909],"li",{},[832,1903,1904],{},"LINEST's reversed order."," The first coefficient belongs to the ",[611,1907,1908],{},"last"," input column; the intercept comes last.",[1900,1911,1912,1915,1916,1918,1919,27],{},[832,1913,1914],{},"Forgetting the intercept column."," ",[14,1917,1882],{}," without a column of ones fits a line through the origin — ",[14,1920,819],{},[1900,1922,1923,1926],{},[832,1924,1925],{},"Correlation is not the slope."," A correlation of 0.8 says the points lie close to a line, not how steep it is.",[1900,1928,1929,1932],{},[832,1930,1931],{},"Collinear inputs."," When two inputs move together almost perfectly, the individual coefficients become unstable even though the fit looks good; their standard errors grow, which is what the second LINEST row is for.",[1900,1934,1935,1938,1939,1941,1942,1945],{},[832,1936,1937],{},"Text in numeric columns."," Excel silently skips text cells in ",[14,1940,16],{},"; pandas may read the whole column as text. Convert with ",[14,1943,1944],{},"pd.to_numeric"," first.",[29,1947,1949],{"id":1948},"conclusion","Conclusion",[10,1951,1952,1954,1955,1957,1958,1960,1961,1963,1964,1966,1967,1969,1970,1973,1974,1976,1977,1979],{},[14,1953,212],{}," is ",[14,1956,16],{}," and ",[14,1959,208],{},", its square is ",[14,1962,82],{},", and ",[14,1965,334],{}," is the whole correlation grid. For regression, ",[14,1968,491],{}," covers the one-input functions, and ",[14,1971,1972],{},"numpy.linalg.lstsq"," with a column of ones covers ",[14,1975,20],{},"; the ",[14,1978,1890],{}," function above reproduces Excel's five-row statistics block, reversed coefficient order included. Check the result against the spreadsheet once, then let Python do the work.",[29,1981,1983],{"id":1982},"frequently-asked-questions","Frequently asked questions",[10,1985,1986,1989,1990,1993,1994,1997],{},[832,1987,1988],{},"What is the pandas equivalent of Excel CORREL?","\nSeries.corr: df",[43,1991,1992],{},"\"x\"",".corr(df",[43,1995,1996],{},"\"y\"",") returns the Pearson correlation coefficient, the same number as CORREL and PEARSON. DataFrame.corr() returns the correlation of every pair of columns.",[10,1999,2000,2003],{},[832,2001,2002],{},"How do I do LINEST in Python?","\nBuild an input matrix with a column of ones for the intercept and solve with numpy.linalg.lstsq. The standard errors, R², F statistic and sums of squares follow from the residuals, as in the linest() function on this page.",[10,2005,2006,2009],{},[832,2007,2008],{},"Why are my LINEST coefficients in the wrong order?","\nExcel's LINEST returns coefficients in the reverse order of the input columns, with the intercept last. NumPy returns them in the order of the columns in your matrix.",[10,2011,2012,2015],{},[832,2013,2014],{},"How is RSQ related to CORREL?","\nRSQ is CORREL squared. It gives the proportion of the variation in y explained by a straight-line fit on x.",[29,2017,2019],{"id":2018},"related","Related",[1897,2021,2022,2028,2035,2042],{},[1900,2023,2024,2025,2027],{},"Up one level: ",[23,2026,26],{"href":25}," — Excel functions translated function by function.",[1900,2029,2030,2034],{},[23,2031,2033],{"href":2032},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fstdev-and-var-equivalents-in-pandas\u002F","STDEV and VAR Equivalents in pandas"," — the sample-versus-population question behind these statistics.",[1900,2036,2037,2041],{},[23,2038,2040],{"href":2039},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-python-in-excel\u002Frun-a-regression-with-python-in-excel\u002F","Run a Regression with Python in Excel"," — the same fit inside a workbook cell.",[1900,2043,2044,2048],{},[23,2045,2047],{"href":2046},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-a-trendline-to-an-excel-chart-with-python\u002F","Add a Trendline to an Excel Chart with Python"," — showing the fitted line on a chart.",[2050,2051,2052],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}",{"title":39,"searchDepth":118,"depth":118,"links":2054},[2055,2056,2057,2058,2059,2060,2061,2062,2063,2064,2065,2066],{"id":31,"depth":118,"text":32},{"id":198,"depth":118,"text":199},{"id":484,"depth":118,"text":485},{"id":617,"depth":118,"text":618},{"id":823,"depth":118,"text":824},{"id":1466,"depth":118,"text":1467},{"id":1750,"depth":118,"text":1751},{"id":1875,"depth":118,"text":1876},{"id":1894,"depth":118,"text":1895},{"id":1948,"depth":118,"text":1949},{"id":1982,"depth":118,"text":1983},{"id":2018,"depth":118,"text":2019},"2026-09-29","Reproduce Excel's CORREL, PEARSON, RSQ and LINEST in pandas and NumPy: one correlation or a whole matrix, simple and multiple regression, the full five-row LINEST statistics block with standard errors, R², F and sums of squares, LINEST's reversed coefficient order, and a check against the spreadsheet's own result.","md",[2071,2073,2075,2077],{"q":1988,"a":2072},"Series.corr: df[\"x\"].corr(df[\"y\"]) returns the Pearson correlation coefficient, the same number as CORREL and PEARSON. DataFrame.corr() returns the correlation of every pair of columns.",{"q":2002,"a":2074},"Build an input matrix with a column of ones for the intercept and solve with numpy.linalg.lstsq. The standard errors, R², F statistic and sums of squares follow from the residuals, as in the linest() function on this page.",{"q":2008,"a":2076},"Excel's LINEST returns coefficients in the reverse order of the input columns, with the intercept last. NumPy returns them in the order of the columns in your matrix.",{"q":2014,"a":2078},"RSQ is CORREL squared. It gives the proportion of the variation in y explained by a straight-line fit on x.","correl and linest equivalents in python",null,{"breadcrumb":2082},[2083,2085,2088],{"name":2084,"item":1058},"Home",{"name":2086,"item":2087},"Advanced Data Transformation and Cleaning for Excel Automation","\u002Fadvanced-data-transformation-and-cleaning\u002F",{"name":26,"item":25},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fcorrel-and-linest-equivalents-in-python",{"title":2091,"description":2092},"Excel CORREL, RSQ and LINEST in Python (pandas and NumPy)","Python equivalents of CORREL, PEARSON, RSQ and LINEST: Series.corr, DataFrame.corr, numpy lstsq for multiple regression, a linest() function returning Excel's 5-row stats block, the reversed coefficient order and verification with LibreOffice.","correl-and-linest-equivalents-in-python","advanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fcorrel-and-linest-equivalents-in-python\u002Findex",[2096,2097],"formula-equivalents","pandas","how-to","I8MNLB8JHg9giHgOYIRAsTb0jPUjPGphGkeL7C9HNxc",[2101,2105],{"title":2102,"path":2103,"stem":2104,"children":-1},"CHOOSE, SWITCH and IFS Equivalents in pandas","\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fchoose-switch-and-ifs-equivalents-in-pandas","advanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fchoose-switch-and-ifs-equivalents-in-pandas\u002Findex",{"title":2106,"path":2107,"stem":2108,"children":-1},"COUNTIF and COUNTIFS Equivalent in pandas","\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fcountif-and-countifs-equivalent-in-pandas","advanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fcountif-and-countifs-equivalent-in-pandas\u002Findex",[2110,2112],{"path":2111,"title":26},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas",{"path":2113,"title":2086},"\u002Fadvanced-data-transformation-and-cleaning",[],[2116,2119,2122,2125,2128,2131],{"path":2117,"title":2118,"kind":2098},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Fvlookup-equivalent-in-pandas-for-excel-files","The VLOOKUP Equivalent in pandas for Excel Files",{"path":2120,"title":2121,"kind":2098},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fextract-numbers-and-codes-from-text-with-pandas","Extract Numbers and Codes from Text with pandas",{"path":2123,"title":2124,"kind":2098},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fsplit-full-names-into-first-and-last-names-with-pandas","Split Full Names into First and Last Names with pandas",{"path":2126,"title":2127,"kind":2098},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fbin-values-into-ranges-with-pandas-cut","Bin Values into Ranges Like Excel FREQUENCY with pd.cut",{"path":2129,"title":2130,"kind":2098},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fcalculate-percent-of-total-per-group-in-pandas","Calculate Percent of Total per Group in pandas",{"path":2132,"title":2133,"kind":2098},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fcount-distinct-values-per-group-in-pandas","Count Distinct Values per Group in pandas",1790972086828]