<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>SAS Users</title>
	<atom:link href="https://blogs.sas.com/content/sgf/feed/" rel="self" type="application/rss+xml" />
	<link>https://blogs.sas.com/content/sgf/</link>
	<description>Providing technical tips and support information, written for and by SAS users.</description>
	<lastBuildDate>Thu, 02 Apr 2026 15:46:46 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.6</generator>
	<item>
		<title>From question to clarity: how SAS Viya Copilot changes the way we work with data</title>
		<link>https://blogs.sas.com/content/sgf/2026/04/01/sas-viya-copilot-data/</link>
					<comments>https://blogs.sas.com/content/sgf/2026/04/01/sas-viya-copilot-data/#respond</comments>
		
		<dc:creator><![CDATA[Sasha Karpinski]]></dc:creator>
		<pubDate>Wed, 01 Apr 2026 14:04:45 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[augmented analytics]]></category>
		<category><![CDATA[Copilot]]></category>
		<category><![CDATA[data quality]]></category>
		<category><![CDATA[SAS Viya]]></category>
		<category><![CDATA[SAS Viya Copilot]]></category>
		<guid isPermaLink="false">https://blogs.sas.com/content/sgf/?p=55258</guid>

					<description><![CDATA[<p>Most analytical journeys start the same way – with a question. When did we have the highest profit? Which customers are driving growth? What segment should we look at next? In traditional analytic workflows, turning those questions into answers often requires navigating menus, configuring visuals, writing calculations and interpreting results [...]</p>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2026/04/01/sas-viya-copilot-data/">From question to clarity: how SAS Viya Copilot changes the way we work with data</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h3><strong>Most analytical journeys start the same way – with a question.</strong></h3>
<p>When did we have the highest profit?<br />
Which customers are driving growth?<br />
What segment should we look at next?</p>
<p>In traditional analytic workflows, turning those questions into answers often requires navigating menus, configuring visuals, writing calculations and interpreting results before a story emerges.</p>
<p><a href="https://blogs.sas.com/content/sascom/2026/02/19/where-insight-keeps-moving-augmented-analytics-inside-sas-visual-analytics/">In the last blog post in this series, we explored how SAS<sup>®</sup> Viya<sup>®</sup> Copilot changes the traditional analytical experience inside SAS<sup>®</sup> Visual Analytics</a>.</p>
<p>Embedded directly in SAS Visual Analytics, Copilot introduces a conversational AI assistant that helps users explore data, create reports, and interpret insights through natural language. By combining generative AI with the analytical capabilities of the SAS Viya platform, Copilot transforms business questions into analytical actions, recommendations, and explanations – creating a more fluid analytics experience.</p>
<p>Let's explore how Viya Copilot can help you move from question to clarity faster than ever.</p>
<h2><strong>Analytics that meet you where you are</strong></h2>
<p>Analytics doesn’t happen in a single step. It unfolds across stages:</p>
<ul>
<li>Exploring and shaping data.</li>
<li>Building visualizations and reports.</li>
<li>Interpreting insights.</li>
</ul>
<p>SAS Viya Copilot supports users throughout this journey – users simply need to ask.</p>
<h2><strong>A more natural way to work with data</strong></h2>
<p>Understanding the data is at the center of answering any business question.  For many users, this early stage of analysis can take time – especially when working with unfamiliar datasets.</p>
<p>Within SAS Visual Analytics, Copilot simplifies this process by acting as a guide to the data – helping users explore, understand and prepare their data for further analysis.</p>
<ul>
<li>To make the data fit-for-purpose, Copilot can change a data item’s properties – including the label, format and aggregation.</li>
<li>Copilot can also assist with common data transformations, including creating groups or bins from categories, defining hierarchies, and preparing data items for time-based or geospatial analysis.</li>
<li>Preparing data often requires creating entirely new calculations to answer a particular question. Instead of manually writing expressions to determine growth rate or profit margin, Copilot can generate new calculations from simple natural language.</li>
</ul>
<p>This conversational interface lowers barriers for business users while accelerating exploration for experienced analysts.</p>
<p><img fetchpriority="high" decoding="async" class="aligncenter size-full wp-image-55273" src="https://blogs.sas.com/content/sgf/files/2026/03/SAS-Viya-Copilot-GIF-Expression.gif" alt="" width="1920" height="1080" /></p>
<h2><strong>From data to visuals – faster</strong></h2>
<p>Designing dashboards and reports often involves a series of manual steps – selecting visuals, assigning data roles, adjusting formatting, and refining layouts. Copilot helps streamline the process, enabling users to go from raw data to meaningful visual stories faster.</p>
<ul>
<li>Copilot can generate new report pages from scratch, automatically creating relevant visuals - like charts, tables, and other content like titles, headers and footers – based on your business question or area of analysis.</li>
<li>Designing dashboards is an iterative process – Copilot can refine existing reports by adding new visuals to the report page, replacing or changing existing visuals to show data in a different way, or deleting visuals or other content that are no longer relevant.</li>
<li>Copilot can focus insights by applying filters, ranks, display rules or interactions to existing visuals, helping narrow attention to the most relevant information.</li>
</ul>
<p>The goal is to remove friction from the workflow so users can work more efficiently. By automating many of the tasks involved in report building, Copilot enables designers to focus their time on communicating the story behind the data.</p>
<p><img decoding="async" class="aligncenter size-full wp-image-55306" src="https://blogs.sas.com/content/sgf/files/2026/04/SAS-Viya-Copilot-GIF-Create-Report-V3-1.gif" alt="" width="1920" height="1080" /></p>
<h2><strong>Insights only a question away</strong></h2>
<p>Reports and dashboards provide insights – and often lead to brand new questions. The conversational nature of Viya Copilot allows users to ask questions about their data and quickly receive tailored on-the-fly responses in real time.</p>
<ul>
<li>Copilot can create ad-hoc visualizations that help answer a user’s question. The categories, measures, and any additional logic – such as applied filters or ranks – are visible to the user, ensuring transparency into how the insight was produced.</li>
<li>New questions often emerge mid-analysis. Copilot retains the conversational context from earlier interactions to handle follow-up questions seamlessly.</li>
<li>Copilot can summarize visualizations, highlight key trends, and generate visual explanations that help users better understand relationships between important variables.</li>
</ul>
<p>In this way, curiosity becomes part of the workflow. Questions surface and can be explored immediately with Copilot acting as a collaborative AI assistant.</p>
<p><img decoding="async" class="aligncenter size-full wp-image-55285" src="https://blogs.sas.com/content/sgf/files/2026/03/SAS-Viya-Copilot-GIF-NLQ.gif" alt="" width="1920" height="1080" /></p>
<h2><strong>A faster path from curiosity to clarity</strong></h2>
<p>SAS Visual Analytics has long included augmented analytics capabilities that automatically surface insights, highlight drivers, and explain outcomes. Viya Copilot builds on that foundation by adding generative AI to the experience.</p>
<p>With Copilot, analytics becomes conversational. This creates a more collaborative relationship between the user and the analytics platform - one where the system actively helps users navigate their analytical journey.</p>
<p>In the next blog post, we’ll focus on the Copilot capabilities available outside of the chat pane – embedding GenAI experiences directly into the user workflow for targeted assistance.</p>
<h3 data-start="791" data-end="889"><strong><a href="https://www.sas.com/en_us/software/viya/copilot.html">Learn more about how SAS Viya Copilot helps organizations turn analytical power into sustained decision momentum.</a></strong></h3>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2026/04/01/sas-viya-copilot-data/">From question to clarity: how SAS Viya Copilot changes the way we work with data</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blogs.sas.com/content/sgf/2026/04/01/sas-viya-copilot-data/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			<enclosure url="https://blogs.sas.com/content/sgf/files/2026/04/SA1101C9-150x150.jpg" />
	</item>
		<item>
		<title>SAS Innovate 2026 and you: the SAS user</title>
		<link>https://blogs.sas.com/content/sgf/2026/03/02/sas-innovate-for-users/</link>
					<comments>https://blogs.sas.com/content/sgf/2026/03/02/sas-innovate-for-users/#respond</comments>
		
		<dc:creator><![CDATA[Chris Hemedinger]]></dc:creator>
		<pubDate>Mon, 02 Mar 2026 22:01:26 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[SAS Global Forum]]></category>
		<category><![CDATA[SAS Innovate]]></category>
		<category><![CDATA[SAS user groups]]></category>
		<guid isPermaLink="false">https://blogs.sas.com/content/sgf/?p=55226</guid>

					<description><![CDATA[<p>SAS Innovate 2026 features content and experiences especially for SAS user group presenters and SAS 9 users.</p>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2026/03/02/sas-innovate-for-users/">SAS Innovate 2026 and you: the SAS user</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>If you're a SAS user who has not been to a SAS conference in a while, I want you to know that <a href="https://www.sas.com/en/events/sas-innovate.html">SAS Innovate 2026</a> was designed with you in mind.</p>
<a href="https://www.sas.com/en/events/sas-innovate.html" class="sc-button sc-button-default"><span><span class="btnheader">REGISTER </span> for SAS Innovate (April 27-30, 2026 in Grapevine TX)</span></a>
<p>Whether your day‑to‑day work happens in SAS 9, SAS Viya, or a thoughtful combination of both, SAS Innovate brings together the people, ideas, and practical guidance that help you get more value from SAS today —- while preparing for what's next.</p>
<p>This year's event doubles down on what SAS users have always valued most: learning from each other, sharing real‑world experience, and finding practical ways to work smarter with the tools you already rely on.</p>
<h2>SAS Users Day: Powered by the community</h2>
<p>One of the highlights of SAS Innovate 2026 is SAS Users Day -- an afternoon focused completely on SAS users as presenters and audience.</p>
<p>SAS Users Day celebrates the way SAS professionals learn best: through user groups, communities, and peer‑to‑peer knowledge sharing.</p>
<p>If you've ever picked up a tip at a local user group meeting, learned something valuable from a SAS Communities post, or borrowed code from a fellow user, SAS Users Day will feel like home. SAS Users Day takes place on Monday afternoon featuring experienced conference presenters, ending with a bit of social time for networking with your fellow users.</p>
<p>Expect:</p>
<ul>
<li>Sessions led by active SAS user group members
</li>
<li>Stories that reflect real constraints, real deadlines, and real wins
</li>
<li>Plenty of opportunities to connect with people who speak your SAS language
</li>
</ul>
<p>It's a reminder that while software matters, the SAS user community is one of the platform's greatest strengths.</p>
<h2>SAS 9: Productive today, Preparing for tomorrow</h2>
<figure id="attachment_55235" aria-describedby="caption-attachment-55235" style="width: 300px" class="wp-caption alignleft"><a href="https://blogs.sas.com/content/sgf/files/2026/03/cooking-sas9.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2026/03/cooking-sas9-300x298.png" alt="" width="300" height="298" class="size-medium wp-image-55235" srcset="https://blogs.sas.com/content/sgf/files/2026/03/cooking-sas9-300x298.png 300w, https://blogs.sas.com/content/sgf/files/2026/03/cooking-sas9-150x150.png 150w, https://blogs.sas.com/content/sgf/files/2026/03/cooking-sas9.png 745w" sizes="(max-width: 300px) 100vw, 300px" /></a><figcaption id="caption-attachment-55235" class="wp-caption-text">"Cooking with SAS 9" - throwback to 2003</figcaption></figure>If SAS 9 is still a big part of how you get work done, this SAS Innovate 2026 agenda is absolutely worth your time. Think of it as a chance to swap notes with people who've been there—sharing practical tips, hard‑won lessons, and smart ways to keep SAS 9 running strong while figuring out what comes next.</p>
<p><a href="https://innovate.sas.com/event/b8642a1b-9ad0-4af8-bc0f-cfac6a57724b/agenda?928f4d96-87ee-4ea5-8cc6-ce26563c56a0_928f4d96=SAS%209">Presenters in the SAS 9 track</a> include current SAS users, as well as developers in R&D's SAS 9 division who support the platform. What you'll learn:</p>
<ul>
<li>How other teams are keeping SAS 9 fast, stable, and reliable for real production workloads
</li>
<li>Simple, proven ways to clean up, manage, and future‑proof long‑lived SAS code
</li>
<li>Where SAS 9 still shines -- and where it makes sense to complement it with newer tools (including AI!)
</li>
<li>Practical patterns for integrating SAS 9 with cloud services, APIs, and open source
</li>
<li>Real stories from customers who are successfully running SAS 9 today (and planning tomorrow)
</li>
</ul>
<p>SAS Innovate works <strong>because of</strong> SAS users -- and it's built to work <strong>for you</strong>. <a href="https://www.sas.com/en/events/sas-innovate.html">Register now, and we'll see you there!</a></p>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2026/03/02/sas-innovate-for-users/">SAS Innovate 2026 and you: the SAS user</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blogs.sas.com/content/sgf/2026/03/02/sas-innovate-for-users/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			<enclosure url="https://blogs.sas.com/content/sgf/files/2026/03/cooking-sas9-150x150.png" />
	</item>
		<item>
		<title>Don&#039;t crash this ship! DuckDB is heading straight for Iceberg!</title>
		<link>https://blogs.sas.com/content/sgf/2026/02/23/sas-duckdb-iceberg/</link>
					<comments>https://blogs.sas.com/content/sgf/2026/02/23/sas-duckdb-iceberg/#respond</comments>
		
		<dc:creator><![CDATA[Joe Cabral]]></dc:creator>
		<pubDate>Mon, 23 Feb 2026 20:37:10 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[DuckDB]]></category>
		<category><![CDATA[Iceberg]]></category>
		<category><![CDATA[Parquet]]></category>
		<category><![CDATA[SAS Viya]]></category>
		<guid isPermaLink="false">https://blogs.sas.com/content/sgf/?p=55163</guid>

					<description><![CDATA[<p>Since its inception, DuckDB has been commanding respect in the data management sphere, carving its place as a highly performant data processing system. At SAS, the rapid advancements DuckDB has made have gone far from unnoticed; that's why, in the 2025.07 release of SAS Viya, we introduced SAS/ACCESS Interface to [...]</p>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2026/02/23/sas-duckdb-iceberg/">Don&#039;t crash this ship! DuckDB is heading straight for Iceberg!</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Since its inception, <a href="https://duckdb.org/">DuckDB</a> has been commanding respect in the data management sphere, carving its place as a highly performant data processing system. At SAS, the rapid advancements DuckDB has made have gone far from unnoticed; that's why, in the 2025.07 release of SAS Viya, we introduced <a href="https://documentation.sas.com/doc/en/pgmsascdc/default/acreldb/p1qjya05qkxd65n1j4pd7ln60lc7.htm">SAS/ACCESS Interface to DuckDB</a>. Over the last seven months, we're proud of the enhancements this has brought to the SAS suite, improving our compatibility and extensibility with open file formats like Parquet, regardless of your choice of data storage. But the job is far from done. In fact, it won't end until we've quacked our last... uh... if quack is the verb... what's the noun?.</p>
<h2>Introducing Iceberg support</h2>
<p>Today I'd like to share a very exciting development to the SAS/ACCESS Interface: <a href="https://go.documentation.sas.com/doc/en/pgmsascdc/default/acreldb/n1d39ixj6e2a5pn1hdtutk0fk3ck.htm">enhanced Iceberg support</a>! As the mountain of data organizations face continues to grow exponentially, it's no surprise that cost-efficient data storage solutions have become a major focus. This focus is two-fold: where do we store our data, for one, and equally important, how do we store our data?</p>
<p>Open file formats like Parquet and Avro are excellent solutions for the latter question, but depending on your choice of storage solution, you might still experience high latency, poor performance, and dangerously high storage costs. Your storage strategy, of course, is likely stratified based on data access frequency and purpose, so it's unlikely to see an organization's entire corpus of data sitting in the same solution. Nonetheless, wherever you keep your data, you'll want your storage affordable and your access efficient. DuckDB and Iceberg, used in tandem, can provide an easy, readable, and efficient methodology for accessing, querying, and even editing data while stored in cost-conscious locations. And now, as of SAS Viya's 2026.01 release, you can wield all that power yourself, from the comfort of your own LIBNAME.</p>
<p>Let's dig in on the what, where, and how!</p>
<h2>Where's on First</h2>
<p>To be clear, this demonstration is surely not the only way to combine SAS, DuckDB, and Iceberg, but I've found it particularly easy, both as an administrator setting it up, and as a user leveraging it. From an admin perspective, the location we'll be keeping our data in today is AWS's S3 - but not the traditional buckets for object storage. Instead, we'll be <a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables-buckets.html">using S3 Table Buckets</a>, which use Apache Iceberg to manage tables as objects, rather than just files. This is supremely important when working with open file formats <a href="https://parquet.apache.org/">like Parquet</a>, because the number one detractor from Parquet is the computational difficulty in editing it. Parquet is highly effective for querying thanks to its columnar nature and paginated metadata, but it requires full file re-writes in order to effect change. Iceberg, as a table format, mitigates this by using metadata files to manage versioning (among many other benefits) of the data they govern. As the top layer, S3 Table Buckets abstract all of these mechanisms away from the user, so all we see are query-ready tables in a conventional database structure.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2026/02/Image.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2026/02/Image.png" alt="" width="1752" height="784" class="alignnone size-full wp-image-55181" srcset="https://blogs.sas.com/content/sgf/files/2026/02/Image.png 1752w, https://blogs.sas.com/content/sgf/files/2026/02/Image-300x134.png 300w, https://blogs.sas.com/content/sgf/files/2026/02/Image-1024x458.png 1024w, https://blogs.sas.com/content/sgf/files/2026/02/Image-768x344.png 768w, https://blogs.sas.com/content/sgf/files/2026/02/Image-1536x687.png 1536w" sizes="(max-width: 1752px) 100vw, 1752px" /></a></p>
<p><a href="https://blogs.sas.com/content/sgf/files/2026/02/Picture1.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2026/02/Picture1.png" alt="" width="790" height="551" class="alignnone size-full wp-image-55184" srcset="https://blogs.sas.com/content/sgf/files/2026/02/Picture1.png 790w, https://blogs.sas.com/content/sgf/files/2026/02/Picture1-300x209.png 300w, https://blogs.sas.com/content/sgf/files/2026/02/Picture1-768x536.png 768w" sizes="(max-width: 790px) 100vw, 790px" /></a></p>
<h2>How's on Second</h2>
<p>By this point, the how is probably pretty clear: the joint operation of the SAS/ACCESS Interface to DuckDB. SAS/ACCESS Interfaces have long been used to connect to remote data sources in SAS 9 and Viya alike. But the DuckDB Interface brings a slightly different modus operandi; rather than being specifically designed to connect to a SINGLE type of data source (i.e. ACCESS to Snowflake, Databricks, etc.), the DuckDB ACCESS Interface can explicitly extend to ANY cloud storage supported <a href="https://duckdb.org/docs/stable/extensions/overview">by DuckDB's vast extension mechanism</a>. This extension mechanism is exactly what we'll be leveraging behind the scenes to establish a connection to Iceberg Tables, in this case those located on S3 Table Buckets.</p>
<p>To connect to Iceberg tables in an S3 Table Bucket, all we need is a LIBNAME statement, just as we would connect to any other remote data source. Here, duckdb is the engine name, and bowser is the library name:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="sas" style="font-family:monospace;"><span style="color: #0000ff;">libname</span> bowser duckdb file_type=iceberg
  iceberg_catalog=<span style="color: #a020f0;">'arn:aws:s3tables:[region]:&lt;account-number&gt;:bucket/[bucket-name]'</span>
  iceberg_endpoint_type=s3_tables
  s3_access_key=<span style="color: #0000ff; font-weight: bold;">&amp;s3key</span>
  s3_secret=<span style="color: #0000ff; font-weight: bold;">&amp;s3secret</span>
  s3_region=<span style="color: #a020f0;">'[region]'</span>
  schema=<span style="color: #a020f0;">'[schema-name]'</span>;</pre></td></tr></table></div>

<p>Here's what you need to make this work:</p>
<ol>
<li>Find the <a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/reference-arns.html">Amazon Resource Name (ARN)</a> of your Table Bucket. This value (in quotes) will be the iceberg_catalog option.
</li>
<li>Declare the file_type option to be iceberg and the iceberg_endpoint_type to be s3_tables. Note that while you can write a large portion of SAS code without any regard to capitalization, the values for these options MUST be lowercase.
</li>
<li>Define your s3_access_key and s3_secret. For general best practice, I used macro variables that I declared elsewhere (in single quotes). These credentials should be tied to a user in your AWS account that has read & write access to the source Table Bucket.
</li>
<li>Define the s3_region of your bucket. It's always good practice to keep your cloud resources in the same region when possible, as cross-regional data movement introduces extra latency.
</li>
<li>The last thing you'll need is the schema value. When looking inside your Table Bucket from the S3 GUI, you'll notice each table has an associated namespace. This value is the schema in the full qualified table location - so any given table in the bucket could be accessed explicitly at: iceberg_catalog.<em>&lt;namespace&gt;.&lt;table&gt;</em>.</li>
</ol>
<p>Once you have all this information, you're ready to test out your new DuckDB-powered Iceberg-backed LIBNAME! You'll notice that just like a traditional Library, your member tables are all present on the left-hand side of the GUI, as seen below. This presence is a slight distinction from some other SAS/ACCESS to DuckDB use cases, where connections to external cloud storage defined within explicit single-transaction PROC SQL / CONNECT statements would, by design, not be considered member tables. I consider the LIBNAME options as a best practice regardless of the data storage to which you’re connecting. Learn more about <a href="https://go.documentation.sas.com/doc/en/pgmsascdc/default/acreldb/n18aa3kusiufghn1p2wr86twu7sr.htm">the options in the LIBNAME statement here</a>.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2026/02/Picture5.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2026/02/Picture5.png" alt="Library view" width="567" height="455" class="alignnone size-full wp-image-55187" srcset="https://blogs.sas.com/content/sgf/files/2026/02/Picture5.png 567w, https://blogs.sas.com/content/sgf/files/2026/02/Picture5-300x241.png 300w" sizes="(max-width: 567px) 100vw, 567px" /></a></p>
<h2>What's on Third</h2>
<p>What I've done to the <a href="https://www.youtube.com/watch?v=sYOUFGfK4bU">classic Abbott & Costello skit</a> at this point is unforgiveable, but I'm in too deep to stop now. Let's get some SAS code on the board and showcase the ease and speed that SAS/ACCESS to DuckDB to bring to your Iceberg tables. Simplest behaviors first: how do we read Iceberg tables?</p>
<p><pre class="preserve-code-formatting">
PROC SQL outobs=10;
  SELECT * from BOWSER.target;
QUIT;
</pre><br />
In the above snippet, we're simply asking DuckDB to return the top 10 rows of the target table within the BOWSER library. It looks no different than any other PROC SQL implicit read. Typically, this Bowser target would be named Mario, but in this case, it's taxi data:</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2026/02/Picture7.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2026/02/Picture7.png" alt="" width="1044" height="179" class="alignnone size-full wp-image-55190" srcset="https://blogs.sas.com/content/sgf/files/2026/02/Picture7.png 1044w, https://blogs.sas.com/content/sgf/files/2026/02/Picture7-300x51.png 300w, https://blogs.sas.com/content/sgf/files/2026/02/Picture7-1024x176.png 1024w, https://blogs.sas.com/content/sgf/files/2026/02/Picture7-768x132.png 768w" sizes="(max-width: 1044px) 100vw, 1044px" /></a></p>
<p>When we were defining the LIBNAME, I mentioned that the S3 Tables each had fully qualified names. This is useful here, because many programmers, including myself, like to use database-specific flavors of SQL. If you're a big fan of DuckDB's SQL flavor syntax, then explicit passthrough in PROC SQL is for you:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="" style="font-family:monospace;">PROC SQL;
  CONNECT USING BOWSER;
  SELECT * FROM CONNECTION TO BOWSER <span class="br0">&#40;</span>
    SELECT * FROM iceberg_catalog.main.target LIMIT <span style="">10</span>
  <span class="br0">&#41;</span>;        
QUIT;</pre></td></tr></table></div>

<p>Using the full name iceberg_catalog.main.target, we can use DuckDB-specific SQL on our S3 Table. Given the <a href="https://blogs.sas.com/content/sgf/2025/12/10/touchduck/">breadth of DuckDB's SQL capabilities</a>, this serves as a powerful connection without too much of a change to the PROC SQL wrapping syntax. Note that in both the implicit and the explicit passthrough scenarios, we are required to pay attention to capitalization for the bucket-specific resources. It doesn’t matter whether the LIBNAME is capitalized, but the case of the schema and table names must match their definition in the bucket.</p>
<p>Thanks to advancements in DuckDB itself at the tail end of 2025, SAS/ACCESS to DuckDB can not only read from Iceberg tables, but also write!</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="" style="font-family:monospace;">PROC SQL;
  CREATE TABLE BOWSER.line_items AS
    SELECT * FROM WORK.line_items;  
QUIT;</pre></td></tr></table></div>

<p>Once again, there's <a href="https://en.wikipedia.org/wiki/Standard_score">no deviation from the standards</a> of PROC SQL in table creation. This exact code might be useful if we wanted to transfer a SAS table (the above being from <a href="https://www.tpc.org/tpch/">the TPC-H datasets</a>) into an Iceberg Table in S3 as part of our storage strategy. In our S3 Table Bucket, we can validate that the new line_items table has been created:</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2026/02/Picture9.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2026/02/Picture9.png" alt="" width="516" height="429" class="alignnone size-full wp-image-55193" srcset="https://blogs.sas.com/content/sgf/files/2026/02/Picture9.png 516w, https://blogs.sas.com/content/sgf/files/2026/02/Picture9-300x249.png 300w" sizes="(max-width: 516px) 100vw, 516px" /></a></p>
<p><a href="https://blogs.sas.com/content/sgf/files/2026/02/Picture10.jpg"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2026/02/Picture10.jpg" alt="" width="713" height="325" class="alignnone size-full wp-image-55196" srcset="https://blogs.sas.com/content/sgf/files/2026/02/Picture10.jpg 713w, https://blogs.sas.com/content/sgf/files/2026/02/Picture10-300x137.jpg 300w" sizes="(max-width: 713px) 100vw, 713px" /></a></p>
<p>Maybe we want to perform some changes to this table down the line. If this table were stored simply as a Parquet file in a bucket, this would be a computationally expensive hassle, but thanks again to Iceberg, edits on these table objects are easy!</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="" style="font-family:monospace;">/* How many records do we have to start? A: <span style="">6001215</span> */
PROC SQL;
  SELECT COUNT<span class="br0">&#40;</span>*<span class="br0">&#41;</span> FROM BOWSER.line_items;
QUIT;
&nbsp;
/* Delete all the records with ship-date before <span style="">1993</span> */
PROC SQL;
  DELETE FROM BOWSER.line_items
    WHERE L_SHIPDAT &lt;= '01JAN1993'd;
QUIT;
&nbsp;
/* Validation: How many records now? A: <span style="">5242432</span> */
PROC SQL;
  SELECT COUNT<span class="br0">&#40;</span>*<span class="br0">&#41;</span> FROM BOWSER.line_items;
QUIT;</pre></td></tr></table></div>

<p><a href="https://blogs.sas.com/content/sgf/files/2026/02/Picture12.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2026/02/Picture12.png" alt="" width="523" height="195" class="alignnone size-full wp-image-55199" srcset="https://blogs.sas.com/content/sgf/files/2026/02/Picture12.png 523w, https://blogs.sas.com/content/sgf/files/2026/02/Picture12-300x112.png 300w" sizes="(max-width: 523px) 100vw, 523px" /></a></p>
<p>In just 8 seconds, DuckDB executed the removal of all records with a shipping date before 1993 from the table. Similarly, we can add or update records with ease. Do note <a href="https://iceberg.apache.org/spec/">the intricacies of versioning and deletions</a> within Iceberg that allow for such efficient editing of tables backed in immutable formats. I consider it important to understand not just the fact that DuckDB and Iceberg together add value, but why they do so behind the scenes.</p>
<p>The one caveat before we check into the performance of SAS/ACCESS to DuckDB + Iceberg is the current limitations on the editing side. With real-world data, adding, updating, and deleting records aren't the only things that happen. Often times, we want to mutate the structure of a table itself: add, update, or remove columns. This is currently not supported in DuckDB itself, though DuckDB.org explicitly lists "schema evolution" in <a href="https://duckdb.org/2025/11/28/iceberg-writes-in-duckdb#conclusion-and-future-work">their near future planning</a>. In the meantime, I highly recommend reading through the November announcement of <a href="https://duckdb.org/2025/11/28/iceberg-writes-in-duckdb">write capabilities in DuckDB</a> itself to learn the full extent of its capabilities.</p>
<h2>Bringing it Home</h2>
<p>I've harped on the value-add of DuckDB & Iceberg from the lens of SAS Viya for three bases now. Let's use a more in-depth query to validate the benefits. We'll use a widely popular (and freely available) dataset: the <a href="https://www.nyc.gov/site/tlc/about/tlc-trip-record-data.page">Yellow Taxi Trip Records</a> courtesy of NYC.gov - specifically a single Iceberg table encompassing data from 2024, titled yellow_tripdata_2024:</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2026/02/Picture13.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2026/02/Picture13.png" alt="" width="1469" height="279" class="alignnone size-full wp-image-55202" srcset="https://blogs.sas.com/content/sgf/files/2026/02/Picture13.png 1469w, https://blogs.sas.com/content/sgf/files/2026/02/Picture13-300x57.png 300w, https://blogs.sas.com/content/sgf/files/2026/02/Picture13-1024x194.png 1024w, https://blogs.sas.com/content/sgf/files/2026/02/Picture13-768x146.png 768w" sizes="(max-width: 1469px) 100vw, 1469px" /></a></p>
<p>The 41 million row dataset occupies 6.27GB sitting as a local .sas7bdat to the Viya environment, backed on a disk with <a href="https://azure.microsoft.com/en-us/pricing/details/netapp/">NetApp Files Premium</a>. For this test, we've gone ahead and pre-converted the dataset into an Iceberg table sitting in the S3 Table Bucket, where it occupies 656MB. With both versions of the dataset available, the test consists of a single implicit PROC SQL statement, seen below:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="" style="font-family:monospace;">PROC SQL;
  SELECT
    passenger_count,
    payment_type,
    count<span class="br0">&#40;</span>*<span class="br0">&#41;</span>            AS num_trips,
    avg<span class="br0">&#40;</span>trip_distance<span class="br0">&#41;</span>  AS avg_distance,
    avg<span class="br0">&#40;</span>fare_amount<span class="br0">&#41;</span>    AS avg_fare,
    avg<span class="br0">&#40;</span>tip_amount<span class="br0">&#41;</span>     AS avg_tip
  FROM
    <span class="br0">&#91;</span>library<span class="br0">&#93;</span>.yellow_tripdata_2024
  WHERE
    passenger_count is not NULL AND
    passenger_count &gt; <span style="">0</span> AND
    passenger_count &lt; <span style="">5</span> AND
    trip_distance &lt; <span style="">100</span> AND
    trip_distance &gt; <span style="">0</span>
  GROUP BY
    passenger_count, payment_type
  ORDER BY
    payment_type, passenger_count; 
QUIT;</pre></td></tr></table></div>

<p>This query serves as a solid litmus test for overall engine performance, leveraging both CPU & I/O through its calculated columns, filtration, and ordering clause. We first ran it using a standard SAS Library connection to the data on the disk; then, we used the DuckDB Library to execute the query against the Iceberg table on the S3 Table Bucket. The test found a sizable improvement in both real-time and CPU-time performance by leveraging the S3 Table Bucket and querying it with the DuckDB engine:</p>
<table>
<thead>
<tr>
<th>Engine + Storage</th>
<th>Real Time (s)</th>
<th>CPU Time (s)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Standard Engine + Azure NetApp Files Premium</td>
<td>25.82</td>
<td>18.96</td>
</tr>
<tr>
<td>DuckDB + Iceberg on S3 Tables Bucket</td>
<td>3.05</td>
<td>4.60</td>
</tr>
<tr>
<td><strong>Efficiency Boost by DuckDB</strong></td>
<td><strong>8.46x</strong></td>
<td><strong>4.12x</strong></td>
</tr>
</tbody>
</table>
<p>Now, while these results are exciting, I want to point out that not every query, not every table, and certainly not every use case will reap the same benefits. For example, some downstream analytical procedures in the SAS software suite that can't be mimicked with a querying engine. But there are tangible, and sometimes massive, performance improvements that SAS/ACCESS to DuckDB can introduce to your pipelines. To boot, the S3 Table Bucket that we used to back this experiment is significantly cheaper than many of the high-performance storage disks. Here's a sample calculation based on current rates and size assumptions:</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2026/02/Picture14.jpg"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2026/02/Picture14.jpg" alt="Savings Calculator" width="713" height="158" class="alignnone size-full wp-image-55205" srcset="https://blogs.sas.com/content/sgf/files/2026/02/Picture14.jpg 713w, https://blogs.sas.com/content/sgf/files/2026/02/Picture14-300x66.jpg 300w" sizes="(max-width: 713px) 100vw, 713px" /></a></p>
<p>Migrating this individual workload not only noticeably improved performance, but it reduced the storage cost of the backing data by just about 99%, while also adding in the aforementioned benefits of Iceberg tables like time travel and versioning.</p>
<p>The last 7 months have brought incredible developments to SAS's integration with open file formats, and I'm personally thrilled to both witness and build with the resulting solutions in the SAS software suite. If you’re interested in learning more about the capabilities of SAS/ACCESS to DuckDB, I highly recommend taking the official SAS course:<a href="https://learn.sas.com/course/view.php?id=8699"> Working with DuckDB in SAS Viya®</a>. It's quick, informative, and shows you a handful of tips, tricks, and best practices for maximizing the value of the engine. As always, thank you for reading, and I’ll see you at home plate.</p>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2026/02/23/sas-duckdb-iceberg/">Don&#039;t crash this ship! DuckDB is heading straight for Iceberg!</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blogs.sas.com/content/sgf/2026/02/23/sas-duckdb-iceberg/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			<enclosure url="https://blogs.sas.com/content/sgf/files/2026/02/DuckDB_logo-150x150.png" />
	</item>
		<item>
		<title>Engage for Managed Cloud Services: Strategic automation for the future</title>
		<link>https://blogs.sas.com/content/sgf/2026/01/09/engage-for-managed-cloud-services-strategic-automation-for-the-future/</link>
		
		<dc:creator><![CDATA[John Conoley]]></dc:creator>
		<pubDate>Fri, 09 Jan 2026 19:37:23 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[cloud computing]]></category>
		<category><![CDATA[deployment]]></category>
		<category><![CDATA[SAS Administrators]]></category>
		<category><![CDATA[SAS Viya]]></category>
		<guid isPermaLink="false">https://blogs.sas.com/content/sgf/?p=55140</guid>

					<description><![CDATA[<p>Engage is a strategic automation framework that enables SAS to deliver cloud services with speed, precision and scalability. Whether supporting custom enterprise deployments or standardized offerings, Engage helps us deliver value faster and more reliably to our hosted customers.</p>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2026/01/09/engage-for-managed-cloud-services-strategic-automation-for-the-future/">Engage for Managed Cloud Services: Strategic automation for the future</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>“Speed it up, lock it down, and cut the clutter. If we’re not moving forward, we’re falling behind.” Some iteration of this message is hard not to feel in today’s world where pressures are rising to innovate and deliver. Fast. </p>
<p>At SAS, our Managed Cloud Services (MCS) division strives to meet these demands head-on through a powerful automation engine: the Engage platform. </p>
<p>Engage is a strategic automation framework that enables SAS to deliver cloud services with speed, precision and scalability. Whether supporting custom enterprise deployments or standardized offerings, Engage helps us deliver value faster and more reliably to our hosted customers. </p>
<h2>What is Engage?</h2>
<p>Engage is SAS’ internal automation platform designed to streamline service delivery across cloud environments. It powers everything from infrastructure provisioning to life cycle management, enabling self-service workflows and near real-time results. Built on modern cloud principles like Infrastructure as Code (IaC), DevOps, FinOps and security governance, Engage is the backbone of our cloud management strategy.  </p>
<p>Within SAS Managed Cloud Services, Engage supports three core service lines: </p>
<ul>
<li>Managed Cloud Services Enterprise: Tailored infrastructure for high-revenue contracts.</li>
<li>
Managed Cloud Services Fleet: Immutable, standardized offerings for scale. </li>
<li>
Managed Cloud Services Developer Experience (DevExp): Automation and governance for internal teams.</li>
</ul>
<p>Let’s explore how each delivers strategic value. </p>
<h2>SAS Managed Cloud Services Enterprise: Custom Infrastructure at Scale </h2>
<p>For customers with complex requirements, SAS Managed Cloud Services Enterprise provides tailored infrastructure and software deployments. Engage automates the sizing and provisioning process by pulling data from each customer’s Cloud Card and translating it into precise infrastructure builds using IaC. </p>
<p>Once the infrastructure is ready, build administrators use Engage to trigger software installations via auto-DaC workflows. This automation reduces deployment timelines and enables flexible customization, accelerating time to value without sacrificing control. </p>
<p>What’s the strategic value? Enterprise customers get access to bespoke environments faster, with fewer manual steps and greater consistency. </p>
<h2>Managed Cloud Services Fleet: Immutable, Pre-Packaged Offerings</h2>
<h2>
<p>Managed Cloud Services Fleet is designed for scale. It delivers standardized SAS® Viya®  environments, like  SAS® Viya® Essentials, that can be provisioned in under two hours. These offerings include pre-sized infrastructure and standardized software orders (e.g., Visual Analytics, Visual Statistics), all delivered through the Engage Catalog. </p>
<p>Because Fleet environments are immutable, lifecycle automation becomes predictable and reliable. Build administrators provide minimal inputs, and Engage handles the rest, right up to identity provider integration. </p>
<p>What’s the strategic value? Fleet enables rapid deployment at scale, making it ideal for SMBs, trials, and repeatable use cases. </p>
</h2>
<h2>Managed Cloud Services Developer Experience: Empowering Automation and Governance </h2>
<p>Behind the scenes, our Developer Experience (DevExp) framework empowers SAS teams to build, deploy, and manage automation across hyperscaler environments like Azure, AWS, and Google Cloud.<br />
DevExp includes: </p>
<ul>
<li>Code repositories and CI/CD pipelines. </li>
<li>
Governance tools for compliance. </li>
<li>
Integration with platforms like Azure DevOps and GitHub Actions. </li>
<li>
Self-service portals via MCAP, ServiceNow and Engage. </li>
<li>
Life cycle management for infrastructure and operations. </li>
<li>A developer control plane for monitoring, logging, security and observability. </li>
</ul>
<p>DevExp supports both Enterprise and Fleet offerings, ensuring automation is standardized, repeatable and scalable. </p>
<p>What’s the strategic value? DevExp enables internal teams to innovate faster while maintaining governance and transparency. </p>
<h2>Continuous Innovation: What’s Next for Engage and Managed Cloud Services</h2>
<p>Our journey with Engage is far from over. The platform is evolving to support AI-infused automation, deeper integrations with hyperscaler services, and expanded partner access. These innovations will further reduce friction, improve customer experience, and unlock new possibilities for service delivery. </p>
<p>As we continually modernize our cloud frameworks, Engage remains central to our strategy, helping us deliver smarter, faster and more secure cloud services.</p>
<p>Engage is setting a new standard for automation in SAS Managed Cloud Services. By combining self-service workflows, lifecycle automation and modern cloud principles, SAS is delivering faster outcomes and better experiences for our customers. </p>
<p>Interested in learning more? Read our white paper, <a href="https://www.sas.com/content/dam/sasdam/documents/20250124/enabling-data-and-analytics-in-the-cloud.pdf"><em>Enabling data and analytics in the cloud.</em></a></p>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2026/01/09/engage-for-managed-cloud-services-strategic-automation-for-the-future/">Engage for Managed Cloud Services: Strategic automation for the future</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></content:encoded>
					
		
		
			<enclosure url="https://blogs.sas.com/content/sgf/files/2026/01/cloud-download-icon-line-connection-circuit-board-150x150.jpg" />
	</item>
		<item>
		<title>Update Data Live in SAS Viya: Integrating SAS Code with Interactive Reports</title>
		<link>https://blogs.sas.com/content/sgf/2026/01/08/update-data-live-in-sas-viya-integrating-sas-code-with-interactive-reports/</link>
		
		<dc:creator><![CDATA[Danny Sprukulis]]></dc:creator>
		<pubDate>Thu, 08 Jan 2026 14:00:01 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<guid isPermaLink="false">https://blogs.sas.com/content/sgf/?p=55023</guid>

					<description><![CDATA[<p>In SAS Viya 4, we can embed inputs directly on the reporting page with live results. These reports have code that takes user inputs and runs the program, which will run the dataset and update it with the most recent data. This gives the user the ability to create datasets [...]</p>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2026/01/08/update-data-live-in-sas-viya-integrating-sas-code-with-interactive-reports/">Update Data Live in SAS Viya: Integrating SAS Code with Interactive Reports</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p style="text-align: left"><span style="color: #000000"><a style="color: #000000" href="https://blogs.sas.com/content/sgf/files/2025/12/WebContentUpdating.jpg"><img loading="lazy" decoding="async" class="wp-image-55053 alignright" src="https://blogs.sas.com/content/sgf/files/2025/12/WebContentUpdating-300x192.jpg" alt="" width="377" height="241" srcset="https://blogs.sas.com/content/sgf/files/2025/12/WebContentUpdating-300x192.jpg 300w, https://blogs.sas.com/content/sgf/files/2025/12/WebContentUpdating-1024x656.jpg 1024w, https://blogs.sas.com/content/sgf/files/2025/12/WebContentUpdating-768x492.jpg 768w, https://blogs.sas.com/content/sgf/files/2025/12/WebContentUpdating.jpg 1096w" sizes="(max-width: 377px) 100vw, 377px" /></a>In SAS Viya 4, we can embed inputs directly on the reporting page with live results. These reports have code that takes user inputs and runs the program, which will run the dataset and update it with the most recent data. This gives the user the ability to create datasets on the fly and share the results with their teams.</span></p>
<p><span style="color: #000000">We begin with our code, which creates macros that can communicate with our report and eventually the job that runs in the background of the report. Below (Figure 1), we can see the variables being brought into the code. They are then assigned values that will be used in our code and formulas (in my example, I have an optimization running in SAS code called Opt_Article.sas, which is where the macros are referenced). The code can then run in whichever program the user would like, but at the end of the program, there must be an upload process to get the final table into the CAS engine. Our table is named Project_Opt. This table will come into play later. For this example I am assigning values to project capacity, which could be used as budgets and time constraints.</span></p>
<figure id="attachment_55056" aria-describedby="caption-attachment-55056" style="width: 386px" class="wp-caption aligncenter"><a style="color: #000000" href="https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating1.png"><img loading="lazy" decoding="async" class="wp-image-55056 " src="https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating1.png" alt="" width="386" height="148" srcset="https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating1.png 318w, https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating1-300x115.png 300w" sizes="(max-width: 386px) 100vw, 386px" /></a><figcaption id="caption-attachment-55056" class="wp-caption-text"><span style="color: #000000">Figure 1: Macro Variables being assigned</span></figcaption></figure>
<p>&nbsp;</p>
<p><span style="color: #000000">For this code to update a report live, the code must finish with an upload statement that takes down the previous dataset and replaces it with the updated dataset with the same name (Figure 2).</span></p>
<figure id="attachment_55059" aria-describedby="caption-attachment-55059" style="width: 615px" class="wp-caption aligncenter"><a style="color: #000000" href="https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating2-1.png"><img loading="lazy" decoding="async" class="wp-image-55059" src="https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating2-1.png" alt="" width="615" height="163" srcset="https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating2-1.png 740w, https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating2-1-300x79.png 300w" sizes="(max-width: 615px) 100vw, 615px" /></a><figcaption id="caption-attachment-55059" class="wp-caption-text"><span style="color: #000000">Figure 2: The table created from the program is called work.solution, it is then fed to a temporary table that exchanges the dataset with Project_Opt, leaving just the most up to date data in Project_Opt</span></figcaption></figure>
<p><span style="color: #000000">The next step is to create a job in SAS. The user must create a new Job Definition and change the form to HTML. In the code, the user will need to create HTML code that reflects the inputs required by the macros. In Figure 3, the HTML for the inputs are shown. These allow the user to provide a base value and title for the input.</span></p>
<figure id="attachment_55050" aria-describedby="caption-attachment-55050" style="width: 620px" class="wp-caption aligncenter"><a style="color: #000000" href="https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating3-1.png"><img loading="lazy" decoding="async" class="wp-image-55050 size-full" src="https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating3-1.png" alt="" width="620" height="208" srcset="https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating3-1.png 620w, https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating3-1-300x101.png 300w" sizes="(max-width: 620px) 100vw, 620px" /></a><figcaption id="caption-attachment-55050" class="wp-caption-text"><span style="color: #000000">Figure 3: HTML of an input variable</span></figcaption></figure>
<p><span style="color: #000000">Then, the program side of the job must reference the code the user created that contains the formulas that utilize the macros (Figure 4).</span></p>
<figure id="attachment_55062" aria-describedby="caption-attachment-55062" style="width: 766px" class="wp-caption aligncenter"><a style="color: #000000" href="https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating4.png"><img loading="lazy" decoding="async" class="wp-image-55062 size-full" src="https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating4.png" alt="" width="766" height="564" srcset="https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating4.png 766w, https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating4-300x221.png 300w" sizes="(max-width: 766px) 100vw, 766px" /></a><figcaption id="caption-attachment-55062" class="wp-caption-text"><span style="color: #000000">Figure 4: Code for referencing the HTML, creating an input interface, and referencing the original program using the macros (Opt_Article.sas)</span></p>
<dd></figcaption></figure>
<p><span style="color: #000000">On the right-hand side of the job interface, the parameters must be selected (Figure 5). This is where the macros need to be reflected for the report, matching the ones entered in the code and HTML. The additional parameters are for debugging purposes.</span></p>
<p><a style="color: #000000" href="https://blogs.sas.com/content/sgf/files/2025/12/Figure5-1.png"><img loading="lazy" decoding="async" class=" wp-image-55065" src="https://blogs.sas.com/content/sgf/files/2025/12/Figure5-1.png" alt="" width="205" height="428" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure5-1.png 248w, https://blogs.sas.com/content/sgf/files/2025/12/Figure5-1-144x300.png 144w" sizes="(max-width: 205px) 100vw, 205px" /></a></p>
<p><span style="color: #000000"><a style="color: #000000" href="https://blogs.sas.com/content/sgf/files/2025/12/Figure5-2.png"><img loading="lazy" decoding="async" class="wp-image-55068 aligncenter" src="https://blogs.sas.com/content/sgf/files/2025/12/Figure5-2.png" alt="" width="301" height="420" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure5-2.png 386w, https://blogs.sas.com/content/sgf/files/2025/12/Figure5-2-215x300.png 215w" sizes="(max-width: 301px) 100vw, 301px" /></a></span></p>
<p><span style="color: #000000"><em>Figure 5: Input Parameters (left) and setting up the parameters (right)</em></span></p>
<p><span style="color: #000000">Once this is completed, the job is ready for use. Copy the URL provided in the properties tab under Job URL (Figure 6).</span></p>
<figure id="attachment_55074" aria-describedby="caption-attachment-55074" style="width: 260px" class="wp-caption aligncenter"><a style="color: #000000" href="https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating6.png"><img loading="lazy" decoding="async" class="size-full wp-image-55074" src="https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating6.png" alt="" width="260" height="840" srcset="https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating6.png 260w, https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating6-93x300.png 93w" sizes="(max-width: 260px) 100vw, 260px" /></a><figcaption id="caption-attachment-55074" class="wp-caption-text"><span style="color: #000000">Figure 6: Under the properties tab, copy the address of the Job URL</span></figcaption></figure>
<p><span style="color: #000000">Then, in a VA report, select the Web Content object. In the options tab of the object, place the URL. The job interface should appear. The user must then bring in the table that is created from the program, Project_Opt (Figure 7).</span></p>
<figure id="attachment_55077" aria-describedby="caption-attachment-55077" style="width: 484px" class="wp-caption aligncenter"><a style="color: #000000" href="https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating7.png"><img loading="lazy" decoding="async" class="size-full wp-image-55077" src="https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating7.png" alt="" width="484" height="394" srcset="https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating7.png 484w, https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating7-300x244.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating7-168x137.png 168w" sizes="(max-width: 484px) 100vw, 484px" /></a><figcaption id="caption-attachment-55077" class="wp-caption-text"><span style="color: #000000">Figure 7: The Web Content Object with the Job URL linked inside of it.</span></figcaption></figure>
<p><span style="color: #000000">The data can now be shown in graphical interfaces. In the options tab for any graph using the table, select Periodically Reload Data and set a short timeframe. Then switch to non-edit mode and run the job (Figure 8).</span></p>
<figure id="attachment_55080" aria-describedby="caption-attachment-55080" style="width: 936px" class="wp-caption aligncenter"><a style="color: #000000" href="https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating8.png"><img loading="lazy" decoding="async" class="size-full wp-image-55080" src="https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating8.png" alt="" width="936" height="454" srcset="https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating8.png 936w, https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating8-300x146.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/webcontentupdating8-768x373.png 768w" sizes="(max-width: 936px) 100vw, 936px" /></a><figcaption id="caption-attachment-55080" class="wp-caption-text"><span style="color: #000000">Figure 8: Final report which will update live after each Submit on the Web Content object. The report must not be in edit mode to get the updates.</span></figcaption></figure>
<p><span style="color: #000000">The report should update as you run the submission, providing the updated dataset and report. This allows the user to share project results live with their teammates and gives non-coders the ability to manipulate the datasets to generate value for their workforce.</span></dd>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2026/01/08/update-data-live-in-sas-viya-integrating-sas-code-with-interactive-reports/">Update Data Live in SAS Viya: Integrating SAS Code with Interactive Reports</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></content:encoded>
					
		
		
			<enclosure url="https://blogs.sas.com/content/sgf/files/2025/12/WebContentUpdating-150x150.jpg" />
	</item>
		<item>
		<title>Uploading and visualizing custom shapefiles in SAS Viya</title>
		<link>https://blogs.sas.com/content/sgf/2026/01/07/shapefiles-in-sas-viya/</link>
		
		<dc:creator><![CDATA[Danny Sprukulis]]></dc:creator>
		<pubDate>Wed, 07 Jan 2026 17:49:28 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[CAS]]></category>
		<category><![CDATA[custom regions]]></category>
		<category><![CDATA[data visualization]]></category>
		<category><![CDATA[geographic visualization]]></category>
		<category><![CDATA[geospatial data]]></category>
		<category><![CDATA[mapping]]></category>
		<category><![CDATA[SAS macros]]></category>
		<category><![CDATA[SAS Visual Analytics]]></category>
		<category><![CDATA[SAS Viya]]></category>
		<category><![CDATA[shapefiles]]></category>
		<guid isPermaLink="false">https://blogs.sas.com/content/sgf/?p=54981</guid>

					<description><![CDATA[<p>Learn how to create and visualize custom geographic regions in SAS Viya Visual Analytics, either by grouping existing map shapes or by uploading and configuring shapefiles to support regions with custom borders.</p>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2026/01/07/shapefiles-in-sas-viya/">Uploading and visualizing custom shapefiles in SAS Viya</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></description>
										<content:encoded><![CDATA[<figure id="attachment_54984" aria-describedby="caption-attachment-54984" style="width: 293px" class="wp-caption alignright"><a href="https://blogs.sas.com/content/sgf/files/2025/12/Photo-Source-Getty-Images.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/Photo-Source-Getty-Images.png" alt="World map" width="293" height="164" class="size-full wp-image-54984" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Photo-Source-Getty-Images.png 1172w, https://blogs.sas.com/content/sgf/files/2025/12/Photo-Source-Getty-Images-300x168.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/Photo-Source-Getty-Images-1024x573.png 1024w, https://blogs.sas.com/content/sgf/files/2025/12/Photo-Source-Getty-Images-768x430.png 768w" sizes="(max-width: 293px) 100vw, 293px" /></a><figcaption id="caption-attachment-54984" class="wp-caption-text">Photo Source: Getty Images</figcaption></figure>
<p>SAS Viya has a large list of mapping shapes for geographic visualizations, but sometimes business units have custom regions that need to be displayed. These can range from individual counties, sales regions, postal codes, and voting regions. To properly visualize these regions, SAS has a process to bring in regions with custom borders.</p>
<p>SAS already has many regions preloaded into the system. When creating a new geography item in Visual Analytics, Viya has three options: Geographic name or code lookup, Geographic data provider, or Latitude and Longitude in data. For building regions that are already in SAS, select the geographic name or code lookup option. A drop-down list will appear asking for a Name or Code lookup (Figure 1).</p>
<figure id="attachment_54987" aria-describedby="caption-attachment-54987" style="width: 614px" class="wp-caption aligncenter"><a href="https://blogs.sas.com/content/sgf/files/2025/12/Figure-1-New-geography-item-selection-screen-selecting-the-name-or-lookup-value.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/Figure-1-New-geography-item-selection-screen-selecting-the-name-or-lookup-value.png" alt="" width="614" height="305" class="size-full wp-image-54987" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure-1-New-geography-item-selection-screen-selecting-the-name-or-lookup-value.png 1638w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-1-New-geography-item-selection-screen-selecting-the-name-or-lookup-value-300x149.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-1-New-geography-item-selection-screen-selecting-the-name-or-lookup-value-1024x508.png 1024w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-1-New-geography-item-selection-screen-selecting-the-name-or-lookup-value-768x381.png 768w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-1-New-geography-item-selection-screen-selecting-the-name-or-lookup-value-1536x761.png 1536w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-1-New-geography-item-selection-screen-selecting-the-name-or-lookup-value-164x82.png 164w" sizes="(max-width: 614px) 100vw, 614px" /></a><figcaption id="caption-attachment-54987" class="wp-caption-text">Figure 1: New geography item selection screen, selecting the name or lookup value</figcaption></figure>
<p>Depending on the user data, the context will change. If the column has full country names, use Country or Region Names. If there are 2 letter state names, use that option. There are quite a few options (Figure 2) but for regions that cover whole states and do not have custom borders/shapes outside of political boundaries, the option of Subdivision (State, province) Names is ideal. When that option is selected, another drop-down will appear for the country that the data refers to, giving worldwide options for region shapes (Figure 3).</p>
<figure id="attachment_54990" aria-describedby="caption-attachment-54990" style="width: 585px" class="wp-caption aligncenter"><a href="https://blogs.sas.com/content/sgf/files/2025/12/Figure-2-Selecting-the-option-for-province-names-in-the-lookup.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/Figure-2-Selecting-the-option-for-province-names-in-the-lookup.png" alt="" width="585" height="408" class="size-full wp-image-54990" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure-2-Selecting-the-option-for-province-names-in-the-lookup.png 780w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-2-Selecting-the-option-for-province-names-in-the-lookup-300x209.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-2-Selecting-the-option-for-province-names-in-the-lookup-768x536.png 768w" sizes="(max-width: 585px) 100vw, 585px" /></a><figcaption id="caption-attachment-54990" class="wp-caption-text">Figure 2: Selecting the option for province names in the lookup</figcaption></figure>
<figure id="attachment_54993" aria-describedby="caption-attachment-54993" style="width: 734px" class="wp-caption aligncenter"><a href="https://blogs.sas.com/content/sgf/files/2025/12/Figure-3-When-the-subdivision-option-is-selected-the-options-will-be-country-specific.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/Figure-3-When-the-subdivision-option-is-selected-the-options-will-be-country-specific.png" alt="" width="734" height="218" class="size-full wp-image-54993" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure-3-When-the-subdivision-option-is-selected-the-options-will-be-country-specific.png 1836w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-3-When-the-subdivision-option-is-selected-the-options-will-be-country-specific-300x89.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-3-When-the-subdivision-option-is-selected-the-options-will-be-country-specific-1024x305.png 1024w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-3-When-the-subdivision-option-is-selected-the-options-will-be-country-specific-768x228.png 768w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-3-When-the-subdivision-option-is-selected-the-options-will-be-country-specific-1536x457.png 1536w" sizes="(max-width: 734px) 100vw, 734px" /></a><figcaption id="caption-attachment-54993" class="wp-caption-text">Figure 3: When the subdivision option is selected, the options will be country specific</figcaption></figure>
<p>The user can now visualize the provinces/states for individual countries (Figure 4). But, sometimes these regions are tied together. To combine these predefined provinces, the user can create a custom category. In the data tab, create a custom category. Add the geography item created before to be “Based on”. Then add each of the provinces to individual groups for each region (Figure 5). Then add a button bar or filter to individually select the regions to display (Figure 6). A data tip value can be added so when hovering over the map, the business region will appear.</p>
<figure id="attachment_54996" aria-describedby="caption-attachment-54996" style="width: 791px" class="wp-caption aligncenter"><a href="https://blogs.sas.com/content/sgf/files/2025/12/Figure-4-A-map-of-Canada-created-using-the-Figure-3-options.-These-shapes-are-preloaded-by-SAS.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/Figure-4-A-map-of-Canada-created-using-the-Figure-3-options.-These-shapes-are-preloaded-by-SAS.png" alt="" width="791" height="438" class="size-full wp-image-54996" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure-4-A-map-of-Canada-created-using-the-Figure-3-options.-These-shapes-are-preloaded-by-SAS.png 1054w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-4-A-map-of-Canada-created-using-the-Figure-3-options.-These-shapes-are-preloaded-by-SAS-300x166.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-4-A-map-of-Canada-created-using-the-Figure-3-options.-These-shapes-are-preloaded-by-SAS-1024x567.png 1024w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-4-A-map-of-Canada-created-using-the-Figure-3-options.-These-shapes-are-preloaded-by-SAS-768x426.png 768w" sizes="(max-width: 791px) 100vw, 791px" /></a><figcaption id="caption-attachment-54996" class="wp-caption-text">Figure 4: A map of Canada created using the Figure 3 options—These shapes are preloaded by SAS</figcaption></figure>
<figure id="attachment_54999" aria-describedby="caption-attachment-54999" style="width: 858px" class="wp-caption aligncenter"><a href="https://blogs.sas.com/content/sgf/files/2025/12/Figure-5-Custom-Category-creating-groups-of-region-shapes.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/Figure-5-Custom-Category-creating-groups-of-region-shapes.png" alt="" width="858" height="395" class="size-full wp-image-54999" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure-5-Custom-Category-creating-groups-of-region-shapes.png 1144w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-5-Custom-Category-creating-groups-of-region-shapes-300x138.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-5-Custom-Category-creating-groups-of-region-shapes-1024x471.png 1024w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-5-Custom-Category-creating-groups-of-region-shapes-768x353.png 768w" sizes="(max-width: 858px) 100vw, 858px" /></a><figcaption id="caption-attachment-54999" class="wp-caption-text">Figure 5: Custom Category creating groups of region shapes</figcaption></figure>
<figure id="attachment_55002" aria-describedby="caption-attachment-55002" style="width: 777px" class="wp-caption aligncenter"><a href="https://blogs.sas.com/content/sgf/files/2025/12/Figure-6-Using-the-custom-category-to-visualize-the-regiongroup-of-provinces.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/Figure-6-Using-the-custom-category-to-visualize-the-regiongroup-of-provinces.png" alt="" width="777" height="435" class="size-full wp-image-55002" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure-6-Using-the-custom-category-to-visualize-the-regiongroup-of-provinces.png 1036w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-6-Using-the-custom-category-to-visualize-the-regiongroup-of-provinces-300x168.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-6-Using-the-custom-category-to-visualize-the-regiongroup-of-provinces-1024x573.png 1024w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-6-Using-the-custom-category-to-visualize-the-regiongroup-of-provinces-768x430.png 768w" sizes="(max-width: 777px) 100vw, 777px" /></a><figcaption id="caption-attachment-55002" class="wp-caption-text">Figure 6: Using the custom category to visualize the region/group of provinces</figcaption></figure>
<p>Now, if the region has custom borders, there is a process for this. To start this process, the user must have Geographic permissions from the admin of the Viya environment (Full admin permissions also work). Once that is completed, they can begin. To start, the user needs to have a shapefile of the region they wish to bring to SAS. The files required for this process to properly run are .shp, .shx, .dbf, and .prj files (Figure 7). These files are used to turn a shapefile into a dataset SAS can read. </p>
<figure id="attachment_55005" aria-describedby="caption-attachment-55005" style="width: 475px" class="wp-caption aligncenter"><a href="https://blogs.sas.com/content/sgf/files/2025/12/Figure-7-Files-needed-to-create-custom-regions-in-SAS.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/Figure-7-Files-needed-to-create-custom-regions-in-SAS.png" alt="" width="475" height="247" class="size-full wp-image-55005" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure-7-Files-needed-to-create-custom-regions-in-SAS.png 792w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-7-Files-needed-to-create-custom-regions-in-SAS-300x156.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-7-Files-needed-to-create-custom-regions-in-SAS-768x400.png 768w" sizes="(max-width: 475px) 100vw, 475px" /></a><figcaption id="caption-attachment-55005" class="wp-caption-text">Figure 7: Files needed to create custom regions in SAS</figcaption></figure>
<p>Once those files are in SAS, a SAS macro will need to be run. This macro (Figure 8) has <> brackets where the user will need to fill out. The shape file path is the path to the .shp file location. The .shp file must be stored with the other 3 files in the same folder. The ID column is the column with the names of the regions in the shapefile dataset. This file is what maps data to the file once in Visual Analytics by mapping the categorical column that contains the region name and joins it with the shapefile names. If the id column is unknown to the user, I have found a way that if the code is run with the other blanks filled, the code will run with errors but the columns in the .shp file are revealed with the rows. To do this, give the &lt;insert ID Column&gt; a random name and once the name of the column is discovered, replace the ID and rerun the code. The outtable is the name that the SAS dataset will take on after the code is run. The cashost and casport are values found in Manage Environment in the Servers tab, just copy those values over. Lastly, the caslib selection is which library the SAS dataset will be outputted in (Public or casuser typically).</p>
<figure id="attachment_55008" aria-describedby="caption-attachment-55008" style="width: 3184px" class="wp-caption aligncenter"><a href="https://blogs.sas.com/content/sgf/files/2025/12/Figure-8-SAS-shapefile-upload-macro.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/Figure-8-SAS-shapefile-upload-macro.png" alt="" width="3184" height="288" class="size-full wp-image-55008" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure-8-SAS-shapefile-upload-macro.png 3184w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-8-SAS-shapefile-upload-macro-300x27.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-8-SAS-shapefile-upload-macro-1024x93.png 1024w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-8-SAS-shapefile-upload-macro-768x69.png 768w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-8-SAS-shapefile-upload-macro-1536x139.png 1536w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-8-SAS-shapefile-upload-macro-2048x185.png 2048w" sizes="(max-width: 3184px) 100vw, 3184px" /></a><figcaption id="caption-attachment-55008" class="wp-caption-text">Figure 8: SAS shapefile upload macro</figcaption></figure>
<p>Once the macro has run successfully, the user can navigate to Visual Analytics. This is where the shapefiles are connected to the dataset. Make sure the user’s data containing the region values is included in the report. Then select “New Data Item” in the data pane and “New Geography Item”. Select the region column that has the region names for the “Based on” item. Then select “Geographic data provider”. This is where the new region shapes are created. If the user has created these regions before, they can use a provider in the drop-down menu. In this case a new provider is needed, so click on the three dot icon. Select “New provider” (Figure 9).</p>
<figure id="attachment_55011" aria-describedby="caption-attachment-55011" style="width: 768px" class="wp-caption aligncenter"><a href="https://blogs.sas.com/content/sgf/files/2025/12/Figure-9-Selecting-the-Geographic-data-provider-and-New-provider-options.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/Figure-9-Selecting-the-Geographic-data-provider-and-New-provider-options.png" alt="" width="768" height="416" class="size-full wp-image-55011" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure-9-Selecting-the-Geographic-data-provider-and-New-provider-options.png 768w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-9-Selecting-the-Geographic-data-provider-and-New-provider-options-300x163.png 300w" sizes="(max-width: 768px) 100vw, 768px" /></a><figcaption id="caption-attachment-55011" class="wp-caption-text">Figure 9: Selecting the Geographic data provider and New provider options</figcaption></figure>
<p>The new geographic data provider menu will appear. Enter a Name and a Label; these can be the same or different. This determines how the provider will appear the next time the user wants to use these shapes. Leave Type and Server the same, unless the shapefile macro was saved elsewhere. Select the Library to where the macro in Figure 8 had caslib pointing to (Public in this case). Select the outtable created for the table selection. Select Polygon for geometry. Change the ID column to the value used in id for Figure 8. Then have _seq_, SEGMENT, Y, X for the next 4 options (Figure 10). Lastly, the coordinate space is needed.</p>
<p>Depending on where the .shp was found, it could be a variety of different values. WGS84 is the standard I like to work with but when shapefiles are sourced from outside of the user’s ArcGIS then custom options can be needed. If the EPSG value is known, select coordinate space as custom value and then “EPSG &lt;4-digit number>”. The user may not get it correct on the first try so keep trying different numbers as the EPSG value (that are somewhat close to the estimated value. Googling the value is an option as well). A wrong EPSG value may put the shapes all over the map but as the guesses get closer, the shapes will appear closer to their actual location. </p>
<figure id="attachment_55014" aria-describedby="caption-attachment-55014" style="width: 447px" class="wp-caption aligncenter"><a href="https://blogs.sas.com/content/sgf/files/2025/12/Figure-10-Inputs-to-create-the-custom-regions-in-Visual-Analytics.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/Figure-10-Inputs-to-create-the-custom-regions-in-Visual-Analytics.png" alt="" width="447" height="555" class="size-full wp-image-55014" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure-10-Inputs-to-create-the-custom-regions-in-Visual-Analytics.png 596w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-10-Inputs-to-create-the-custom-regions-in-Visual-Analytics-242x300.png 242w" sizes="(max-width: 447px) 100vw, 447px" /></a><figcaption id="caption-attachment-55014" class="wp-caption-text">Figure 10: Inputs to create the custom regions in Visual Analytics</figcaption></figure>
<p>Once this is complete, the provider will be created. Now returning to the geography item page, select ID column as the same as the “Based on” column. Then the regions are complete. Select a geo-region map in the objects pane and select the new geography item as the geography (Figure 11).</p>
<figure id="attachment_55017" aria-describedby="caption-attachment-55017" style="width: 678px" class="wp-caption aligncenter"><a href="https://blogs.sas.com/content/sgf/files/2025/12/Figure-11-The-visualized-custom-regions.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/Figure-11-The-visualized-custom-regions.png" alt="" width="678" height="482" class="size-full wp-image-55017" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure-11-The-visualized-custom-regions.png 904w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-11-The-visualized-custom-regions-300x213.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-11-The-visualized-custom-regions-768x545.png 768w, https://blogs.sas.com/content/sgf/files/2025/12/Figure-11-The-visualized-custom-regions-269x192.png 269w" sizes="(max-width: 678px) 100vw, 678px" /></a><figcaption id="caption-attachment-55017" class="wp-caption-text">Figure 11: The visualized custom regions</figcaption></figure>
<p>The new shapes are properly uploaded into SAS and can be visualized with data. The users now have the freedom to map any territory they prefer and can customize their mapping reports. By combining SAS Viya’s built-in geographic options with the ability to upload custom shapefiles, users can accurately represent business-specific regions that go beyond standard political boundaries. Whether you are grouping existing regions or bringing in fully custom shapes, this process gives you the flexibility to create precise, meaningful maps that better reflect how your organization views and analyzes geographic data.</p>
<h3>Learn more</h3>
<ul>
<li><a href="https://blogs.sas.com/content/sgf/2025/12/16/mapping-data-over-images/">Mapping data over images</a></li>
<li><a href="https://blogs.sas.com/content/sgf/2025/01/10/utilizing-sas-viyas-geocoding-capabilities-for-multiple-data-configurations-and-languages/">Utilizing SAS Viya’s Geocoding capabilities for multiple data configurations and languages</a></li>
<li><a href="https://blogs.sas.com/content/sgf/2024/12/20/simulating-theme-park-wait-times-within-sas-viya-creating-a-live-solution-for-hospitality-operations/">Simulating theme park wait times within SAS Viya, creating a live solution for hospitality operations</a></li>
</ul>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2026/01/07/shapefiles-in-sas-viya/">Uploading and visualizing custom shapefiles in SAS Viya</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></content:encoded>
					
		
		
			<enclosure url="https://blogs.sas.com/content/sgf/files/2025/12/Figure-11-The-visualized-custom-regions-150x150.png" />
	</item>
		<item>
		<title>The afterparty: Hyperparameter autotuning revisited</title>
		<link>https://blogs.sas.com/content/sgf/2025/12/17/the-afterparty-hyperparameter-autotuning-revisited/</link>
		
		<dc:creator><![CDATA[Stu Sztukowski]]></dc:creator>
		<pubDate>Wed, 17 Dec 2025 21:21:52 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[Cross-Validation]]></category>
		<category><![CDATA[Deep Learning]]></category>
		<category><![CDATA[Higgs boson]]></category>
		<category><![CDATA[Hyperparameter Tuning]]></category>
		<category><![CDATA[machine learning]]></category>
		<category><![CDATA[model interpretability]]></category>
		<category><![CDATA[model optimization]]></category>
		<category><![CDATA[neural networks]]></category>
		<category><![CDATA[SAS Viya Workbench]]></category>
		<guid isPermaLink="false">https://blogs.sas.com/content/sgf/?p=54849</guid>

					<description><![CDATA[<p>In my first article on hyperparameter autotuning, I used a cake analogy to show how to use hyperparameter autotuning with Optuna and the sasviya.ml package in Python to improve detecting Higgs bosons in a particle accelerator. SAS Viya Workbench now supports hyperparameter autotuning in SAS code with a variety of [...]</p>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2025/12/17/the-afterparty-hyperparameter-autotuning-revisited/">The afterparty: Hyperparameter autotuning revisited</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><span style="color:#000000">In </span><a href="https://blogs.sas.com/content/sgf/2025/06/13/boost-ml-accuracy-with-hyperparameter-tuning/">my first article on hyperparameter autotuning</a><span style="color:#000000">, I used a cake analogy to show how to use hyperparameter autotuning with Optuna and the sasviya.ml package in Python to improve detecting Higgs bosons in a particle accelerator. SAS Viya Workbench now supports hyperparameter autotuning in SAS code with a variety of different machine learning models, helping you achieve greater accuracy with less code. Remember how using Optuna was like asking Gordon Ramsay to critique our cake? In Python, we had to get him into the kitchen and tell him what to critique and how to critique it. This time, </span><em style="color:#000000">he’s already here and ready to yell at you</em><span style="color:#000000">.</span></p>
<p>Let’s take a look at the absolute minimum amount of code needed to perform hyperparameter autotuning with a gradient boosting model using SAS code.</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="sas" style="font-family:monospace;"><span style="color: #000080; font-weight: bold;">proc gradboost</span> <span style="color: #000080; font-weight: bold;">data</span>=sashelp.iris;
    autotune;
    target species / level=nominal;
    <span style="color: #0000ff;">input</span> <span style="color: #0000ff;">_NUMERIC_</span>;
<span style="color: #000080; font-weight: bold;">run</span>;</pre></td></tr></table></div>

<p>Yep. That’s it. It’s literally one extra line: autotune.</p>
<p>When you add this single line, SAS will run it through a genetic algorithm, partition it into a 70/30 train/validation split, run multiple parallel evaluations in a single iteration, and automatically stop after either 50 evaluations, 5 iterations, 10 hours, or stagnation in 4 iterations. Each hyperparameter has pre-configured default bounds and is fully modifiable. If you’re curious as to what these defaults are, <a href="https://go.documentation.sas.com/doc/en/workbenchcdc/v_001/vwbcasml/vwbcasml_gradboost_syntax02.htm">check out the documentation for the AUTOTUNE statement in PROC GRADBOOST</a>.</p>
<p>That’s a whole lot of things happening from that one little statement. This gives you a good baseline, and considering how well-tuned the default values are, it may end up being all you need.</p>
<h2>Predicting Higgs bosons with SAS</h2>
<p>Let’s go back to solving the <a href="https://archive.ics.uci.edu/dataset/280/higgs">Higgs boson prediction problem</a>, and this time we’ll do it with SAS code. One thing I love about programming in SAS is how compact the code is for modeling. To show you how little code is needed to solve this problem in its entirety, I’m going to post the whole program below.</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="sas" style="font-family:monospace;"><span style="color: #0000ff;">filename</span> inpipe pipe <span style="color: #a020f0;">&quot;unzip -p /workspaces/myfolder/data/higgs.zip | gunzip -c&quot;</span>;
&nbsp;
<span style="color: #0000ff;">%let</span> colnames = signal lepton_pt lepton_eta lepton_phi 
                missing_energy_magnitude missing_energy_phi
                jet_1_pt jet_1_eta jet_1_phi jet_1_btag
                jet_2_pt jet_2_eta jet_2_phi jet_2_btag
                jet_3_pt jet_3_eta jet_3_phi jet_3_btag
                jet_4_pt jet_4_eta jet_4_phi jet_4_btag
                m_jj m_jjj m_lv m_jlv m_bb m_wbb m_wwbb
                ;
&nbsp;
<span style="color: #000080; font-weight: bold;">data</span> higgs;
    <span style="color: #0000ff;">infile</span> inpipe dlm=<span style="color: #a020f0;">','</span> dsd;
    <span style="color: #0000ff;">input</span> <span style="color: #0000ff; font-weight: bold;">&amp;colnames</span>;
    <span style="color: #0000ff;">drop</span> m_:;
<span style="color: #000080; font-weight: bold;">run</span>;
&nbsp;
<span style="color: #000080; font-weight: bold;">proc gradboost</span> <span style="color: #000080; font-weight: bold;">data</span>=higgs outmodel=low_level_model seed=<span style="color: #2e8b57; font-weight: bold;">42</span> earlystop<span style="color: #66cc66;">&#40;</span>stagnation=<span style="color: #2e8b57; font-weight: bold;">0</span><span style="color: #66cc66;">&#41;</span>;
    partition fraction<span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">validate</span>=<span style="color: #2e8b57; font-weight: bold;">0.15</span> test=<span style="color: #2e8b57; font-weight: bold;">0.15</span> seed=<span style="color: #2e8b57; font-weight: bold;">42</span><span style="color: #66cc66;">&#41;</span>;
&nbsp;
    autotune 
        objective=auc 
        searchmethod=bayesian 
        targetevent=<span style="color: #a020f0;">'1'</span>
        popsize=<span style="color: #2e8b57; font-weight: bold;">5</span>
        historytable=higgs_low_level_history
        tuningparameters = <span style="color: #66cc66;">&#40;</span>
             ntrees<span style="color: #66cc66;">&#40;</span>UB=<span style="color: #2e8b57; font-weight: bold;">300</span><span style="color: #66cc66;">&#41;</span> 
             maxdepth<span style="color: #66cc66;">&#40;</span>UB=<span style="color: #2e8b57; font-weight: bold;">30</span><span style="color: #66cc66;">&#41;</span>
             learningrate<span style="color: #66cc66;">&#40;</span>UB=<span style="color: #2e8b57; font-weight: bold;">0.1</span><span style="color: #66cc66;">&#41;</span>
        <span style="color: #66cc66;">&#41;</span>
    ;
&nbsp;
    target signal / level=nominal;
    <span style="color: #0000ff;">input</span> <span style="color: #0000ff;">_NUMERIC_</span>;
&nbsp;
    <span style="color: #0000ff;">output</span> out=low_level_preds role;
<span style="color: #000080; font-weight: bold;">run</span>;</pre></td></tr></table></div>

<p>That’s a grand total of 38 lines, including some extra whitespace for readability. In those 38 lines, we:</p>
<ul>
<li>Define where our file is and how to unpack with unzip and gunzip:

<div class="wp_syntax"><table><tr><td class="code"><pre class="sas" style="font-family:monospace;"><span style="color: #0000ff;">filename</span> inpipe pipe <span style="color: #a020f0;">&quot;unzip -p /workspaces/myfolder/data/higgs.zip | gunzip -c&quot;</span>;</pre></td></tr></table></div>

</li>
</ul>
<ul>
<li>Define our column names and their order:

<div class="wp_syntax"><table><tr><td class="code"><pre class="sas" style="font-family:monospace;"><span style="color: #0000ff;">%let</span> colnames = ...;</pre></td></tr></table></div>

</li>
</ul>
<ul>
<li>Unzip, untar, and read in the data:

<div class="wp_syntax"><table><tr><td class="code"><pre class="sas" style="font-family:monospace;"><span style="color: #000080; font-weight: bold;">data</span> higgs;
    <span style="color: #0000ff;">infile</span> inpipe dlm=<span style="color: #a020f0;">','</span> dsd;
    <span style="color: #0000ff;">input</span> <span style="color: #0000ff; font-weight: bold;">&amp;colnames</span>;
    <span style="color: #0000ff;">drop</span> m_:;
<span style="color: #000080; font-weight: bold;">run</span>;</pre></td></tr></table></div>

</li>
</ul>
<p>Then, <strong>in one gradient boosting procedure</strong>:</p>
<ul>
<li>Perform a repeatable train/validate/test split:

<div class="wp_syntax"><table><tr><td class="code"><pre class="sas" style="font-family:monospace;">partition fraction<span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">validate</span>=<span style="color: #2e8b57; font-weight: bold;">0.15</span> test=<span style="color: #2e8b57; font-weight: bold;">0.15</span> seed=<span style="color: #2e8b57; font-weight: bold;">42</span><span style="color: #66cc66;">&#41;</span></pre></td></tr></table></div>

</li>
<li>Enable autotuning:

<div class="wp_syntax"><table><tr><td class="code"><pre class="sas" style="font-family:monospace;">autotune</pre></td></tr></table></div>

</li>
<li>Set our autotuning objective, search method, and target event:

<div class="wp_syntax"><table><tr><td class="code"><pre class="sas" style="font-family:monospace;">objective=auc
searchmethod=bayesian
targetevent=<span style="color: #a020f0;">'1'</span></pre></td></tr></table></div>

</li>
<li>Create 4 parallel evaluations, with 5 iterations and 5 evaluations per iteration:

<div class="wp_syntax"><table><tr><td class="code"><pre class="sas" style="font-family:monospace;">popsize=<span style="color: #2e8b57; font-weight: bold;">5</span></pre></td></tr></table></div>

</li>
<li>Save the best model, autotuning history, predictions, and tag the role of each prediction:

<div class="wp_syntax"><table><tr><td class="code"><pre class="sas" style="font-family:monospace;">outmodel=low_level_model 
historytable=higgs_low_level_history 
<span style="color: #0000ff;">output</span> out=low_level_preds role</pre></td></tr></table></div>

</li>
<li>Set a few hyperparameter bounds to try because we learned last time that more trees, lower depth, and a lower learning rate are important with this data:

<div class="wp_syntax"><table><tr><td class="code"><pre class="sas" style="font-family:monospace;">tuningparameters = <span style="color: #66cc66;">&#40;</span>
    ntrees<span style="color: #66cc66;">&#40;</span>UB=<span style="color: #2e8b57; font-weight: bold;">300</span><span style="color: #66cc66;">&#41;</span> 
    maxdepth<span style="color: #66cc66;">&#40;</span>UB=<span style="color: #2e8b57; font-weight: bold;">30</span><span style="color: #66cc66;">&#41;</span>
    learningrate<span style="color: #66cc66;">&#40;</span>UB=<span style="color: #2e8b57; font-weight: bold;">0.1</span><span style="color: #66cc66;">&#41;</span>
<span style="color: #66cc66;">&#41;</span></pre></td></tr></table></div>

</li>
</ul>
<p>Even though we changed the bounds of a few of our hyperparameters, we are still tuning the rest of the parameters as well. We just gave it some more suggestions.</p>
<p><a href="https://go.documentation.sas.com/doc/en/workbenchcdc/v_001/vwbcasml/vwbcasml_introcom_sect004.htm">There are a ton of other autotuning options available</a>. We can go much further than this, including excluding hyperparameters from tuning, <a href="https://go.documentation.sas.com/doc/en/workbenchcdc/v_001/vwbcasml/vwbcasml_introcom_sect004.htm#vwbcasml.introcom.autotune_secondobjective">adding a second objective</a>, setting a maximum time, using k-fold cross validation, setting a stagnation threshold, and much more.</p>
<p>Not only that, but PROC GRADBOOST handles both classification and regression in only one PROC. You simply tell it what type of target you have, and it sorts out the rest. You’ll find this to be the case for the rest of the machine learning PROCs too, such as <a href="https://go.documentation.sas.com/doc/en/workbenchcdc/v_001/vwbcasml/vwbcasml_nnet_toc.htm">Deep Neural Networks</a> and <a href="https://go.documentation.sas.com/doc/en/workbenchcdc/v_001/vwbcasml/vwbcasml_svmachine_toc.htm">Support Vector Machines</a>. This simplifies the world of models you need to know.</p>
<p>Let’s run this model and see how it compares.</p>
<h2>The results</h2>
<p>How did we do this time around? Using the SAS autotuning framework, <strong>we achieved a test AUC of 0.801 for the low-level model</strong>. Let’s put this into perspective with the rest of the models we were against, including our previous SAS model that was tuned with Optuna.</p>
<table width="690">
<tbody>
<tr>
<td width="400"><strong>Model</strong></td>
<td width="168"><strong>AUC: Low-level</strong></td>
<td width="168"><strong>Δ vs SAS (%)</strong></td>
</tr>
<tr>
<td width="400"><strong>SAS: Tuned Gradient Boosting</strong></td>
<td width="168"><strong>0.801</strong></td>
<td width="168"><strong> </strong></td>
</tr>
<tr>
<td width="400"><strong>SAS: Tuned Gradient Boosting (Optuna)</strong></td>
<td width="168"><strong>0.795 </strong></td>
<td width="168"><strong>-0.75%</strong></td>
</tr>
<tr>
<td width="400">Paper: Boosted Decision Tree</td>
<td width="168">0.73</td>
<td width="168"><strong>-8.9%</strong></td>
</tr>
<tr>
<td width="400">Paper: Shallow Neural Network</td>
<td width="168">0.733</td>
<td width="168"><strong>-8.5%</strong></td>
</tr>
<tr>
<td width="400">Paper: Deep Neural Network</td>
<td width="168">0.880</td>
<td width="168"><strong>+9.9%</strong></td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p>Here’s what all the evaluations looked like:</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/tunedmodel.png"><img loading="lazy" decoding="async" class="aligncenter wp-image-54945" src="https://blogs.sas.com/content/sgf/files/2025/12/tunedmodel.png" alt="" width="500" height="368" srcset="https://blogs.sas.com/content/sgf/files/2025/12/tunedmodel.png 640w, https://blogs.sas.com/content/sgf/files/2025/12/tunedmodel-300x221.png 300w" sizes="(max-width: 500px) 100vw, 500px" /></a></p>
<p>Overall, impressive results for how little code we needed, and we even got our best result just 9 evaluations in. This shows how well SAS has tuned its hyperparameter autotuning framework, looking at optimal combinations of hyperparameters to test without the end-user trying to find a good starting point for each one.</p>
<h2>Going bigger</h2>
<p>If you’re ready to scale out with huge data, you don’t need to learn a whole new language. Many SAS modeling procedures automatically change where the calculations happen, bringing massively parallel, multithreaded processing to your data without any extra effort on your part. All you need to do is load your data to CAS and change your input dataset, then SAS handles the rest.<br />
Don’t believe me? Give it a try yourself on SAS Viya with our first example using Iris:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="sas" style="font-family:monospace;">cas; caslib <span style="color: #0000ff;">_ALL_</span> assign;
&nbsp;
<span style="color: #000080; font-weight: bold;">data</span> casuser.iris;
    <span style="color: #0000ff;">set</span> sashelp.iris;
<span style="color: #000080; font-weight: bold;">run</span>;
&nbsp;
<span style="color: #000080; font-weight: bold;">proc gradboost</span> <span style="color: #000080; font-weight: bold;">data</span>=casuser.iris;
    autotune;
    target species / level=nominal;
    <span style="color: #0000ff;">input</span> <span style="color: #0000ff;">_NUMERIC_</span>;
<span style="color: #000080; font-weight: bold;">run</span>;</pre></td></tr></table></div>

<p>All we did was load the data into CAS and tell PROC GRADBOOST that our data is no longer on disk, but in CAS. Just like that, the processing is now massively parallel and ready to scale to your demands.</p>
<h2>Wrapping it up</h2>
<p>We went through two examples of using sasviya.ml’s GradientBoostingClassifier and PROC GRADBOOST, showing that you can get impressive results in either language. You may be wondering which one is better.</p>
<p>Want to know a secret?</p>
<p><em>They’re both using the exact same engine</em>.</p>
<p>sasviya.ml isn’t some watered down set of algorithms for Python data scientists. Far from it. This is the same <a href="https://blogs.sas.com/content/sascom/2025/06/19/sas-the-workhorse-of-the-ai-era/">high-performance, trusted, battle-tested math</a> that backs other SAS Viya machine learning models. The SAS Viya Python API is designed to be Pythonic and <a href="https://go.documentation.sas.com/doc/en/workbenchcdc/v_001/vwbpygs/p018t2lxmjxl0un1k9c3vp0fgayi.htm">work with other open source packages</a>, letting you seamlessly integrate your favorite packages in the open source ecosystem, like Optuna, with virtually no learning curve.</p>
<p>The flexibility of SAS Viya Workbench gives you the option to run your models in multiple languages while using the same underlying engine. Whether you’re using Python or a PROC, you can be assured that you’re going to get the performance and results that you expect from SAS. The language you use, and <a href="https://blogs.sas.com/content/sgf/2025/04/18/from-slopes-to-stats/">how you mix them together</a>, is entirely up to you.</p>
<h2>Links</h2>
<ul>
<li>Previous article: <a href="https://blogs.sas.com/content/sgf/2025/06/13/boost-ml-accuracy-with-hyperparameter-tuning">Boost ML accuracy with hyperparameter tuning (with a fun twist)</a></li>
<li>Documentation: <a href="https://go.documentation.sas.com/doc/en/vdmmlcdc/v_032/vdmmladvug/n0qns21st1lscpn193aethl4zwi2.htm">Overview of Hyperparameter Autotuning in SAS Viya</a></li>
<li><a href="https://archive.ics.uci.edu/dataset/280/higgs">UC Irvine Machine Learning Repository - Higgs boson dataset</a></li>
</ul>
<h2>References</h2>
<ul>
<li>Whiteson, D. (2014). HIGGS [Dataset]. UCI Machine Learning Repository. <a href="https://doi.org/10.24432/C5V312">https://doi.org/10.24432/C5V312</a>. Licensed under <a href="https://creativecommons.org/licenses/by-sa/4.0">CC BY-SA 4.0</a>.</li>
</ul>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2025/12/17/the-afterparty-hyperparameter-autotuning-revisited/">The afterparty: Hyperparameter autotuning revisited</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></content:encoded>
					
		
		
			<enclosure url="https://blogs.sas.com/content/sgf/files/2025/12/tuning-150x150.png" />
	</item>
		<item>
		<title>Mapping data over images</title>
		<link>https://blogs.sas.com/content/sgf/2025/12/16/mapping-data-over-images/</link>
		
		<dc:creator><![CDATA[Danny Sprukulis]]></dc:creator>
		<pubDate>Tue, 16 Dec 2025 20:39:06 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[ArcGIS]]></category>
		<category><![CDATA[Geo Map]]></category>
		<category><![CDATA[SAS Visual Analytics]]></category>
		<category><![CDATA[SAS Viya]]></category>
		<guid isPermaLink="false">https://blogs.sas.com/content/sgf/?p=54783</guid>

					<description><![CDATA[<p>Mapping in SAS Viya gives the user plenty of options for maps to use. Signing in to ESRI on SAS Viya gives us many more. But what if the map that is needed is not available and is technically not a map at all? Well, not to worry, as there is [...]</p>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2025/12/16/mapping-data-over-images/">Mapping data over images</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></description>
										<content:encoded><![CDATA[<figure id="attachment_54792" aria-describedby="caption-attachment-54792" style="width: 300px" class="wp-caption alignright"><a href="https://blogs.sas.com/content/sgf/files/2025/12/map.jpg"><img loading="lazy" decoding="async" class="wp-image-54792 size-medium" src="https://blogs.sas.com/content/sgf/files/2025/12/map-300x207.jpg" alt="map with pin point" width="300" height="207" srcset="https://blogs.sas.com/content/sgf/files/2025/12/map-300x207.jpg 300w, https://blogs.sas.com/content/sgf/files/2025/12/map-768x531.jpg 768w, https://blogs.sas.com/content/sgf/files/2025/12/map.jpg 977w" sizes="(max-width: 300px) 100vw, 300px" /></a><figcaption id="caption-attachment-54792" class="wp-caption-text">Source: Getty Images</figcaption></figure>
<p>Mapping in SAS Viya gives the user plenty of options for maps to use. Signing in to ESRI on SAS Viya gives us many more. But what if the map that is needed is not available and is technically not a map at all? Well, not to worry, as there is a solution for that. Mapping on images is a lot like creating shapefiles on regular maps; the main difference is how that map is uploaded to SAS. This solution gives the user full control of what they would like to see in their reports and gives them full customizability for mapping visualizations.</p>
<p>The first step is having an ArcGIS Online account. This account needs to be set up with some credits to use as data storage currency so that the image the user uploads can be saved in the system storage. Once this step is complete, the user must also have a desktop version of ArcGIS Pro, where the maps can be built. Within the desktop application, the user must be connected to their ArcGIS Online account through the sign-in page to ensure everything is connected.</p>
<p>Now the user can begin the process. Once in ArcGIS Pro, a new project must be created. This will bring up a map of the world. In the contents pane, right-click <strong>Map</strong> and select <strong>Add Data</strong>. A box will pop up to browse the files, and the user can select the image they wish to use (Figure 1). For this example, the user will be using an image of a stadium.</p>
<figure id="attachment_54795" aria-describedby="caption-attachment-54795" style="width: 455px" class="wp-caption aligncenter"><img loading="lazy" decoding="async" class="wp-image-54795" src="https://blogs.sas.com/content/sgf/files/2025/12/Figure1.png" alt="" width="455" height="303" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure1.png 516w, https://blogs.sas.com/content/sgf/files/2025/12/Figure1-300x200.png 300w" sizes="(max-width: 455px) 100vw, 455px" /><figcaption id="caption-attachment-54795" class="wp-caption-text">Figure 1: Selecting the image file in ArcGIS Pro</figcaption></figure>
<p>The image name will pop up below Map on the Contents pane, and a warning will say “Unknown Coordinate System”, which is fine since the user will be specifying their own coordinates for the image. To provide these coordinates, go to the Imagery tab at the top of the application, from there Imagery options should appear (Figure 2).</p>
<figure id="attachment_54798" aria-describedby="caption-attachment-54798" style="width: 338px" class="wp-caption aligncenter"><img loading="lazy" decoding="async" class="wp-image-54798 size-full" src="https://blogs.sas.com/content/sgf/files/2025/12/Figure2.png" alt="" width="338" height="112" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure2.png 338w, https://blogs.sas.com/content/sgf/files/2025/12/Figure2-300x99.png 300w" sizes="(max-width: 338px) 100vw, 338px" /><figcaption id="caption-attachment-54798" class="wp-caption-text">Figure 2: Imagery tab</figcaption></figure>
<p style="text-align: left">Right-click on the image name in the contents pane. Select “<strong>Zoom to Layer</strong>” and the user will be brought to where the picture is on the map (without a coordinate system, the image should originate in the middle of the Atlantic Ocean). In the Imagery tab, select the “<strong>Geo Reference</strong>” option. Then the system will allow the user to begin geo-referencing the image. The user must now choose where they would like the image to appear on a map. The user must select the “<strong>Add Control Points</strong>” option in the ribbon. Then the user can select a point on the picture. Here, a notable corner on the image is selected (Figure 3).</p>
<figure id="attachment_54801" aria-describedby="caption-attachment-54801" style="width: 455px" class="wp-caption aligncenter"><img loading="lazy" decoding="async" class="wp-image-54801" src="https://blogs.sas.com/content/sgf/files/2025/12/Figure3.png" alt="" width="455" height="282" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure3.png 936w, https://blogs.sas.com/content/sgf/files/2025/12/Figure3-300x186.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/Figure3-768x476.png 768w" sizes="(max-width: 455px) 100vw, 455px" /><figcaption id="caption-attachment-54801" class="wp-caption-text">Figure 3: Selecting a source point on the map that can be easily identified on the base map</figcaption></figure>
<p>Once the initial point is selected, the user must then select a point on the map that this control point connects to (Figure 4).</p>
<figure id="attachment_54804" aria-describedby="caption-attachment-54804" style="width: 450px" class="wp-caption aligncenter"><a href="https://blogs.sas.com/content/sgf/files/2025/12/Figure4.png"><img loading="lazy" decoding="async" class="wp-image-54804" src="https://blogs.sas.com/content/sgf/files/2025/12/Figure4.png" alt="" width="450" height="223" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure4.png 492w, https://blogs.sas.com/content/sgf/files/2025/12/Figure4-300x149.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/Figure4-164x82.png 164w" sizes="(max-width: 450px) 100vw, 450px" /></a><figcaption id="caption-attachment-54804" class="wp-caption-text">Figure 4: Select the identifiable point on a map for a target point</figcaption></figure>
<p>The image will now be placed over the point in its original orientation (Figure 5).</p>
<figure id="attachment_54807" aria-describedby="caption-attachment-54807" style="width: 455px" class="wp-caption aligncenter"><img loading="lazy" decoding="async" class="wp-image-54807" src="https://blogs.sas.com/content/sgf/files/2025/12/Figure5-351x185.png" alt="" width="455" height="302" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure5-300x199.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/Figure5-768x510.png 768w, https://blogs.sas.com/content/sgf/files/2025/12/Figure5.png 936w" sizes="(max-width: 455px) 100vw, 455px" /><figcaption id="caption-attachment-54807" class="wp-caption-text">Figure 5: The image is now connecting the target and source points. The image is still in its upwards position since it will default to that position when not enough control points are active.</figcaption></figure>
<p>The user must add another control point to get the image to follow the proper orientation of its place on a map. To add another control point, select the point on the image as a source point and then select another point on the map as a target point (Figure 6).</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/Figure6.png"><img loading="lazy" decoding="async" class="aligncenter wp-image-54810 size-medium" src="https://blogs.sas.com/content/sgf/files/2025/12/Figure6-300x292.png" alt="" width="300" height="292" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure6-300x292.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/Figure6.png 514w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<p>The image should follow its proper orientation now, but further reference points may be needed to be exact. In my experience, 3-4 is the maximum number of points needed, as the image will start to have distortion and become less accurate.</p>
<p>The image is now mapped and needs to be saved. Right-click on the Map in the Contents pane and select <strong>Properties</strong>. Give the Map a name, then close the Properties tab. The new name should be reflected where the word "Map" used to be. Right-click the new map name and select “<strong>Edit Metadata</strong>”. The user should now give the Item a Title, some tags (brief descriptions of the map), and a summary. Select “<strong>Apply</strong>” in the ribbon and close the metadata page. The user can now upload this image to their ArcGIS Online account. Go to the <strong>Share</strong> tab and select <strong>Web Layer – Publish Web Layer</strong> (Figure 7).</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-54813" src="https://blogs.sas.com/content/sgf/files/2025/12/Figure7.png" alt="" width="392" height="274" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure7.png 392w, https://blogs.sas.com/content/sgf/files/2025/12/Figure7-300x210.png 300w" sizes="(max-width: 392px) 100vw, 392px" /></p>
<p style="text-align: center"><em>Figure 7: Publish Web Layer location</em></p>
<p>A share page will pop up. Select <strong>Layer Type</strong> to be <strong>Tile</strong> and <strong>Share with Everyone</strong>. Then select <strong>Publish</strong>. The image will now be uploaded to the ArcGIS Online account.</p>
<p>In ArcGIS Online, go to the <strong>My Content page</strong>. Find the name of the map that was just created, and the file should say “<strong>Tile Layer (hosted)</strong>”. Select the map, and an overview of the map will pop up. Go to <strong>Settings</strong> and scroll to <strong>Tile Layer (hosted)</strong>. This is where the user chooses how detailed the image will be on the map's scale. In Figure 8, <strong>World to Country</strong> is the default, but depending on the size of the image, the user should change the dropdown from <strong>Country</strong>. For instance, if it is a map of a building, then go to the size of a small building; if it is a large park select the option that is closest to that size.</p>
<p style="text-align: center"><img loading="lazy" decoding="async" class="aligncenter wp-image-54816 " src="https://blogs.sas.com/content/sgf/files/2025/12/Figure8.png" alt="" width="539" height="195" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure8.png 936w, https://blogs.sas.com/content/sgf/files/2025/12/Figure8-300x108.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/Figure8-768x277.png 768w" sizes="(max-width: 539px) 100vw, 539px" /><em style="font-size: 14px">Figure 8: Selecting the Visible Range for the image, depending on the size of the image, the user can change how far out it can be seen from viewing the map</em></p>
<p>Next, do not select <strong>Build Tiles</strong>. First, select <strong>Save</strong> below the visible range selector (Figure 9).</p>
<p style="text-align: center"><img loading="lazy" decoding="async" class="aligncenter wp-image-54819 " src="https://blogs.sas.com/content/sgf/files/2025/12/Figure9.png" alt="" width="551" height="317" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure9.png 750w, https://blogs.sas.com/content/sgf/files/2025/12/Figure9-300x173.png 300w" sizes="(max-width: 551px) 100vw, 551px" /><em style="font-size: 14px">Figure 9: Saving the tile</em></p>
<p>Once saved, select <strong>Build Tiles</strong>. The tile screen will appear, and once it has finished uploading, select <strong>Create Tiles</strong> (Figure 10).</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/Figure10.png"><img loading="lazy" decoding="async" class="aligncenter wp-image-54822 " src="https://blogs.sas.com/content/sgf/files/2025/12/Figure10.png" alt="" width="468" height="333" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure10.png 620w, https://blogs.sas.com/content/sgf/files/2025/12/Figure10-300x214.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/Figure10-269x192.png 269w" sizes="(max-width: 468px) 100vw, 468px" /></a></p>
<p style="text-align: center"><em>Figure 10: Creating the tiles</em></p>
<p>From there, our tile/image is uploaded to the ArcGIS cloud. To use this in SAS Viya, go into <strong>SAS Viya Explore and Visualize</strong>. The user must be signed into their ArcGIS Online account from there. If they are not, they can go to their user icon while in Explore and Visualize, select <strong>Settings</strong>, and select <strong>Geographic Mapping</strong>, and sign in there. Once signed in, bring a map into the Explore and Visualize workspace. This can be any of the maps in the <strong>Objects</strong> tab. In the options on the right-hand side, select <strong>Maps</strong> (Figure 11).</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/Figure11.png"><img loading="lazy" decoding="async" class="aligncenter wp-image-54825 size-medium" src="https://blogs.sas.com/content/sgf/files/2025/12/Figure11-300x127.png" alt="" width="300" height="127" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure11-300x127.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/Figure11.png 464w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<p style="text-align: center"><em>Figure 11: Choosing a map, it will default an Automatic map</em></p>
<p>The user’s maps will show up under <strong>User Tiles</strong> here. Select the file folder icon and a list of mapping options become available to the user (Figure 12). Since the user created a tile in ArcGIS Online, the image will be in that folder.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/Figure12.png"><img loading="lazy" decoding="async" class="aligncenter wp-image-54828 size-medium" src="https://blogs.sas.com/content/sgf/files/2025/12/Figure12-300x110.png" alt="" width="300" height="110" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure12-300x110.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/Figure12-768x282.png 768w, https://blogs.sas.com/content/sgf/files/2025/12/Figure12.png 778w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<p style="text-align: center"><em>Figure 12: All mapping folders available. The ArcGIS.com folders only appear if the user is signed into their ArcGIS Online account in Viya</em></p>
<p>When the image is selected, it will populate within the map. Depending on the <strong>Visual Range</strong> selected in Figure 10, the user may need to zoom to the proper level to see the map. These maps can have shapefiles overlap the image as well. I have added shapefiles to each of the sections of the stadium, so the map is more interactive with data (Figure 13).</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-54831 " src="https://blogs.sas.com/content/sgf/files/2025/12/Figure13.png" alt="" width="567" height="343" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure13.png 936w, https://blogs.sas.com/content/sgf/files/2025/12/Figure13-300x181.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/Figure13-768x464.png 768w" sizes="(max-width: 567px) 100vw, 567px" /></p>
<p style="text-align: center"><em>Figure 13: The final map result in Visual Analytics using a geo-region map</em></p>
<p>Another example of the image mapping is in Figure 14, with mapped body sections over a diagram of the human body.</p>
<p style="text-align: center"><img loading="lazy" decoding="async" class="aligncenter wp-image-54834" src="https://blogs.sas.com/content/sgf/files/2025/12/Figure14.png" alt="" width="522" height="320" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Figure14.png 702w, https://blogs.sas.com/content/sgf/files/2025/12/Figure14-300x184.png 300w" sizes="(max-width: 522px) 100vw, 522px" /><em style="font-size: 14px;text-align: center">Figure 14: An image of the human body being mapped with custom shapefiles of body sections.</em></p>
<p>This is how the user can map over top of images. It allows them to customize their mapping interface for full freedom in their report visualizations.</p>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2025/12/16/mapping-data-over-images/">Mapping data over images</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></content:encoded>
					
		
		
			<enclosure url="https://blogs.sas.com/content/sgf/files/2025/12/map-150x150.jpg" />
	</item>
		<item>
		<title>TouchDuck! Exploring unique features of SAS/ACCESS to DuckDB through college football</title>
		<link>https://blogs.sas.com/content/sgf/2025/12/10/touchduck/</link>
					<comments>https://blogs.sas.com/content/sgf/2025/12/10/touchduck/#comments</comments>
		
		<dc:creator><![CDATA[Joe Cabral]]></dc:creator>
		<pubDate>Wed, 10 Dec 2025 20:26:00 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[data ingestion]]></category>
		<category><![CDATA[DuckDB]]></category>
		<category><![CDATA[PIVOT statement]]></category>
		<category><![CDATA[PROC SQL]]></category>
		<category><![CDATA[SAS Viya]]></category>
		<category><![CDATA[SAS/ACCESS]]></category>
		<guid isPermaLink="false">https://blogs.sas.com/content/sgf/?p=54616</guid>

					<description><![CDATA[<p>Using college football recruiting and talent data as an example, let's see how DuckDB’s flexibility and SAS integration streamline complex transformations and queries.</p>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2025/12/10/touchduck/">TouchDuck! Exploring unique features of SAS/ACCESS to DuckDB through college football</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>There's a chill in the air through much of the United States, and that can only mean one thing: the cold disappointment of your college football season as your team's playoff hopes fade away into the darkness. Back in September, I was curious about what data was readily available surrounding the CFB landscape: recruiting, rankings, gametime statistics, etc. Thanks to the brilliant data collection and export tools produced by <em><a href="https://collegefootballdata.com/">CollegeFootballData.com</a></em>, I was able to build a personalized series of datasets describing several areas of interest:</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/CollegeFootballData.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/CollegeFootballData.png" alt="" width="1084" height="418" class="aligncenter size-full wp-image-54724" srcset="https://blogs.sas.com/content/sgf/files/2025/12/CollegeFootballData.png 1084w, https://blogs.sas.com/content/sgf/files/2025/12/CollegeFootballData-300x116.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/CollegeFootballData-1024x395.png 1024w, https://blogs.sas.com/content/sgf/files/2025/12/CollegeFootballData-768x296.png 768w" sizes="(max-width: 1084px) 100vw, 1084px" /></a></p>
<p>I was originally eyeing a predictive pipeline where I would use SAS Viya to make predictions on the season, but as the games distracted me from my code, I watched a wickedly entertaining (and arguably unlikely) season shake out before my eyes. I decided to pivot from a predictive approach to a more exploratory pipeline, and there's no better way to attack this than with SAS Viya's new ACCESS Engine to DuckDB. In this blog, I'll use my exploratory data analysis of college football data from the 2024-2025 off-season to highlight some neat features of DuckDB that can now be leveraged by the SAS/ACCESS Engine. Without further delay, let's dive into our data.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Tables-in-SAS.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Tables-in-SAS.png" alt="" width="357" height="265" class="alignright size-full wp-image-54634" srcset="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Tables-in-SAS.png 357w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Tables-in-SAS-300x223.png 300w" sizes="(max-width: 357px) 100vw, 357px" /></a></p>
<h2>Data Ingestion</h2>
<p>Before I start even viewing my datasets, I took the path of least resistance for ingestion and simply uploaded the datasets from my PC to the SAS Server on the Viya environment.</p>
<p>One neat capability of DuckDB is its versatility in connecting to data sources; if I were dealing with much larger scale datasets, I might have stored them on an S3 bucket, which SAS/ACCESS to DuckDB can query from with ease using DuckDB's installable extensions. That won't be the case here given the small-scale data at hand. As with any SAS pipeline, the first code we'll execute will set up a Library connection using the DuckDB Engine:</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Football-LIBNAME.jpg"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Football-LIBNAME.jpg" alt="" width="816" height="355" class="aligncenter size-full wp-image-54637" srcset="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Football-LIBNAME.jpg 816w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Football-LIBNAME-300x131.jpg 300w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Football-LIBNAME-768x334.jpg 768w" sizes="(max-width: 816px) 100vw, 816px" /></a></p>
<p>Like most SAS/ACCESS Engines, DuckDB <em>LIBNAME</em> statements have plenty of options for configuring your connection. In this case, all I've done is set an upper bound for the memory usage of the database and select a local path on the SAS Server for the DuckDB database file. A full list of options can be found <a href="https://go.documentation.sas.com/doc/no/pgmsascdc/v_069/acreldb/n18aa3kusiufghn1p2wr86twu7sr.htm">here</a>. Common configurations include the <em>file_path</em> and <em>file_type</em> options, which would instruct the library to load all files of a given type in the defined directory. I've opted against this as a best practice, since I don't want to load all my tables, nor will I want all the columns for every table. I'd rather only load what I need, when I need it—and DuckDB affords me that capability as well.</p>
<h2>Loading And Exploring Our Tables</h2>
<p>Based on what I exported from the College Football Database, I want to start with a "base table" of teams and their characteristics. Let's investigate the <em>teams.csv</em> table and see what we might want from it.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Football-Teams-Load.jpg"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Football-Teams-Load.jpg" alt="" width="801" height="262" class="aligncenter size-full wp-image-54643" srcset="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Football-Teams-Load.jpg 801w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Football-Teams-Load-300x98.jpg 300w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Football-Teams-Load-768x251.jpg 768w" sizes="(max-width: 801px) 100vw, 801px" /></a></p>
<p>Using <em>PROC SQL</em>, I push-down a <em>DESCRIBE SELECT *</em> statement into the DuckDB instance, instructing it to tell me about the CSV dataset columns by just reading one line. In that sense, it's a SQL-writer's parallel to <em>PROC CONTENTS</em>. </p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Teams-Contents.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Teams-Contents.png" alt="" width="447" height="405" class="aligncenter size-full wp-image-54646" srcset="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Teams-Contents.png 447w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Teams-Contents-300x272.png 300w" sizes="(max-width: 447px) 100vw, 447px" /></a></p>
<p>Well, that's a lot of columns, and I'm not sure I care about them all right now. Let's use <em>PROC SQL</em> to load what we want into the <em>DUKLIB</em> library:</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Teams-Load.jpg"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Teams-Load.jpg" alt="" width="841" height="478" class="aligncenter size-full wp-image-54649" srcset="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Teams-Load.jpg 841w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Teams-Load-300x171.jpg 300w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Teams-Load-768x437.jpg 768w" sizes="(max-width: 841px) 100vw, 841px" /></a></p>
<p>I'm doing a few different things in one procedure here. First, I use the <em>CONNECT</em> statement (as done previously) to tell SAS which data source to connect to (the DuckDB library, of course). The <em>EXECUTE</em> statement <em>explicitly</em> pushes SQL code down to DuckDB, meaning that I can write anything I want in <a href="https://duckdb.org/docs/stable/sql/introduction">DuckDB's flavor of SQL</a> (closely related to the PostgreSQL dialect). This dialect allows us to leverage tons of features unavailable to the standard <em>PROC SQL syntax</em>. An easy but significant example here is the <em>CREATE OR REPLACE</em> syntax, which, if ran implicitly in <em>PROC SQL</em> on a standard SAS library, would not be recognized! This is obviously nice since I continually, without fail, forget to drop tables when I edit and re-run queries.</p>
<p>At the bottom of the query, the <em>SELECT * FROM CONNECTION</em> is used to generate an output in the SAS GUI. All <em>EXECUTE</em> statements do just as implied—they execute code in DuckDB—but their results aren't propagated back up to the SAS user interface. If you put a <em>SELECT</em> statement inside an <em>EXECUTE</em> statement, DuckDB would run it, but you, the SAS user, wouldn't see the results. Thus, the <em>SELECT * FROM CONNECTION</em> is what we use to retrieve results from a query.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Teams-Results.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Teams-Results.png" alt="" width="624" height="630" class="aligncenter size-full wp-image-54652" srcset="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Teams-Results.png 624w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Teams-Results-297x300.png 297w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Teams-Results-150x150.png 150w" sizes="(max-width: 624px) 100vw, 624px" /></a></p>
<p>We now have a <em>TEAMS</em> table under <em>DUKLIB</em>, which we can see in our Library pane, just as we would with any other type of data source:</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/TEAMS-Table.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/TEAMS-Table.png" alt="" width="1024" height="322" class="aligncenter size-full wp-image-54730" srcset="https://blogs.sas.com/content/sgf/files/2025/12/TEAMS-Table.png 1024w, https://blogs.sas.com/content/sgf/files/2025/12/TEAMS-Table-300x94.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/TEAMS-Table-768x242.png 768w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></p>
<p>I've talked a lot for just loading a single table, but it's important to lay the groundwork of how the syntax works when using <em>PROC SQL</em> with DuckDB. The benefits are substantial, especially for large and easily <em>subsettable</em> datasets, but—as with any new tool—there’s a learning curve to mastering the syntax. Let's move on and focus on some more data.</p>
<h2>Investigation #1: Talent Gain & Loss Over the Offseason</h2>
<p>In the modern age of <a href="https://247sports.com/season/2025-football/transferportal/">transfer portals</a>, NIL, and ever-increasingly wild coaching carousels, the number one burning question I had of my data was "who gained and who lost talent over the offseason?" I'm not interested in just the transfers, but also the graduates and the incoming freshmen. I have two CSV files pertaining to this, <em>2024_247_composite.csv</em> and <em>2025_247_composite.csv</em>, containing data procured from the <em><a href="https://247sports.com/">247 sports</a></em> news site.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Composite-Ingest-1.jpg"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Composite-Ingest-1.jpg" alt="" width="865" height="286" class="aligncenter size-full wp-image-54658" srcset="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Composite-Ingest-1.jpg 865w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Composite-Ingest-1-300x99.jpg 300w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Composite-Ingest-1-768x254.jpg 768w" sizes="(max-width: 865px) 100vw, 865px" /></a></p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Composite-Ingest-2.jpg"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Composite-Ingest-2.jpg" alt="" width="859" height="526" class="aligncenter size-full wp-image-54661" srcset="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Composite-Ingest-2.jpg 859w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Composite-Ingest-2-300x184.jpg 300w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Composite-Ingest-2-768x470.jpg 768w" sizes="(max-width: 859px) 100vw, 859px" /></a></p>
<p>The first <em>PROC SQL</em> mimics what we did for the <em>TEAMS</em> dataset, and tells us we have three columns: “Year”, “Team”, and “Talent.” Knowing that we have two files, one for 2024 and one for 2025, I join BOTH tables to the <em>DUKLIB.TEAMS</em> table on account of team name, then create a calculated column, “Talent_Delta”, of the difference between the <em>247</em>-assessed talent metrics. Lastly, I use a <em>SELECT * FROM CONNECTION</em> statement to preview my new dataset, <em>DUKLIB.TALENT</em>.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/DUKLIB.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/DUKLIB.png" alt="" width="1156" height="386" class="aligncenter size-full wp-image-54736" srcset="https://blogs.sas.com/content/sgf/files/2025/12/DUKLIB.png 1156w, https://blogs.sas.com/content/sgf/files/2025/12/DUKLIB-300x100.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/DUKLIB-1024x342.png 1024w, https://blogs.sas.com/content/sgf/files/2025/12/DUKLIB-768x256.png 768w" sizes="(max-width: 1156px) 100vw, 1156px" /></a></p>
<p>So far, everything I've done has been in <em>PROC SQL</em>, which makes sense, since DuckDB, after all, is operated with SQL. But can I leverage other <em>PROC's</em> from SAS' toolbox as I would with a different library connection? The answer is a resounding YES. Let's create two tables in our ephemeral <em>WORK</em> library by using <em>PROC SORT</em> on our <em>DUKLIB.TALENT</em> table, ordering our FBS teams by “Talent_Delta” in one output, and by “Talent_2025” in another.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Talent-PROCSORT.jpg"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Talent-PROCSORT.jpg" alt="" width="822" height="228" class="aligncenter size-full wp-image-54670" srcset="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Talent-PROCSORT.jpg 822w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Talent-PROCSORT-300x83.jpg 300w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Talent-PROCSORT-768x213.jpg 768w" sizes="(max-width: 822px) 100vw, 822px" /></a></p>
<h3>Resulting <em>DELTASORT</em> Table:</h3>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/DELTASORT-Top.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/DELTASORT-Top.png" alt="" width="1416" height="500" class="aligncenter size-full wp-image-54742" srcset="https://blogs.sas.com/content/sgf/files/2025/12/DELTASORT-Top.png 1416w, https://blogs.sas.com/content/sgf/files/2025/12/DELTASORT-Top-300x106.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/DELTASORT-Top-1024x362.png 1024w, https://blogs.sas.com/content/sgf/files/2025/12/DELTASORT-Top-768x271.png 768w" sizes="(max-width: 1416px) 100vw, 1416px" /></a></p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/DELTASORT-Bottom.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/DELTASORT-Bottom.png" alt="" width="1616" height="444" class="aligncenter size-full wp-image-54745" srcset="https://blogs.sas.com/content/sgf/files/2025/12/DELTASORT-Bottom.png 1616w, https://blogs.sas.com/content/sgf/files/2025/12/DELTASORT-Bottom-300x82.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/DELTASORT-Bottom-1024x281.png 1024w, https://blogs.sas.com/content/sgf/files/2025/12/DELTASORT-Bottom-768x211.png 768w, https://blogs.sas.com/content/sgf/files/2025/12/DELTASORT-Bottom-1536x422.png 1536w" sizes="(max-width: 1616px) 100vw, 1616px" /></a></p>
<p>You would expect the big Power 4 names to top the <em>DELTASORT</em> table, but, as we'll see in the <em>TALENTSORT</em> table, it's hard to post such large gains when your team was already so talented AND losing players to graduation and transfers. Sympathies do go out to Charlotte, who rounds out the bottom after being virtually gutted over the offseason. Perhaps that partially explains their 1-9 record on the year:</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/Score.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/Score.png" alt="" width="1276" height="534" class="aligncenter size-full wp-image-54751" srcset="https://blogs.sas.com/content/sgf/files/2025/12/Score.png 1276w, https://blogs.sas.com/content/sgf/files/2025/12/Score-300x126.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/Score-1024x429.png 1024w, https://blogs.sas.com/content/sgf/files/2025/12/Score-768x321.png 768w" sizes="(max-width: 1276px) 100vw, 1276px" /></a></p>
<p>In terms of raw talent, the familiar leaders emerge atop the <em>TALENTSORT</em> table:</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/TALENTSORT.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/TALENTSORT.png" alt="" width="1188" height="518" class="aligncenter size-full wp-image-54754" srcset="https://blogs.sas.com/content/sgf/files/2025/12/TALENTSORT.png 1188w, https://blogs.sas.com/content/sgf/files/2025/12/TALENTSORT-300x131.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/TALENTSORT-1024x446.png 1024w, https://blogs.sas.com/content/sgf/files/2025/12/TALENTSORT-768x335.png 768w" sizes="(max-width: 1188px) 100vw, 1188px" /></a></p>
<p>Writing this blog across October and November, I find this simple sort shows the pure beauty of college football: talent doesn't guarantee anything. Check <strong>#6 LSU</strong>, or <strong>#9 Penn State</strong>, or <strong>#12 Auburn</strong>, or <strong>#13 Florida</strong>, all three of which have missed their expectations so significantly that they've <a href="https://www.espn.com/college-football/story/_/id/46615017/2025-college-football-coaching-carousel-firings-hirings">paid millions of dollars</a> <em>just to get rid of their coaches</em>. My condolences go to the fanbases, but after all, it's the suffering that builds character.</p>
<h2>Investigation #2: Offseason Recruiting</h2>
<p>With a general idea of who gained "talent" between seasons, let's dive deeper and focus on the actual <em>recruiting</em> process. Down the line, it would surely be interesting to line that data up next to a list of publicly known NIL funds. I'd bet on a strong correlation. Money talks, in the end.</p>
<p>Anyways, let's investigate the recruiting using the <em>2025_recruiting_detailed.csv</em> dataset. Just as in the previous investigation, I'll use the <em>DUKLIB</em> connection to select the results of a <em>DESCRIBE</em> statement to get the metadata on our columns. </p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Recruiting-Read.jpg"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Recruiting-Read.jpg" alt="" width="915" height="457" class="aligncenter size-full wp-image-54688" srcset="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Recruiting-Read.jpg 915w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Recruiting-Read-300x150.jpg 300w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Recruiting-Read-768x384.jpg 768w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Recruiting-Read-164x82.jpg 164w" sizes="(max-width: 915px) 100vw, 915px" /></a></p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Recruiting-Columns.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Recruiting-Columns.png" alt="" width="580" height="275" class="aligncenter size-full wp-image-54691" srcset="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Recruiting-Columns.png 580w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Recruiting-Columns-300x142.png 300w" sizes="(max-width: 580px) 100vw, 580px" /></a></p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-PositionGroup-List.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-PositionGroup-List.png" alt="" width="156" height="273" class="alignright size-full wp-image-54694" /></a></p>
<p>The number one thing that sticks out to me after running the <em>DESCRIBE</em> statement alone is the <em>PositionGroup</em> variable—what ARE the Position Groups of recruiting, anyway? The <em>SELECT DISTINCT</em> statement/clause will parse exactly that.</p>
<p>The results tell us we have eight total Position Groups, and a summative group as well. That means every team has multiple records pertaining to their recruiting efforts (number of commits, average rating of commits) in different Position Groups, along with a cumulative record. I could simply use a <em>WHERE</em> clause to pull the cumulative records only, but that's no fun, and we'd lose plenty of valuable information—even though we don't need data to see that <strong>your school forgot to recruit a quarterback</strong>. Thankfully, DuckDB provides a key functionality that will allow us to transform this tall dataset into a wide format that will join one-to-one cleanly with our teams & talent data. Let's start by binning some of the Position Groups into categories:</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-PositionGroup-Binning.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-PositionGroup-Binning.png" alt="" width="936" height="575" class="aligncenter size-full wp-image-54697" srcset="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-PositionGroup-Binning.png 936w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-PositionGroup-Binning-300x184.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-PositionGroup-Binning-768x472.png 768w" sizes="(max-width: 936px) 100vw, 936px" /></a></p>
<p>The <em>CASE</em> clause within the <em>EXECUTE</em> statement does exactly what SQL programmers (and Python programmers alike) would expect—it buckets different situations into different results, and saves those results to a new column, called "Category." Here, we isolate four main categories, which closely align with the "three phases of the game": under Defense, we keep "Defensive Back," "Defensive Line," and "Linebacker." Under Offense, we keep "Offensive Line," "Receiver," and "Running Back." Quarterback and Special Teams get their own categories, the former of which could easily slot under Offense, but might be worth considering as its own entity due to its sheer impact on a team's performance. Here's what our <em>DUKLIB.RECRUITING_RAW</em> table looks like:</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Recruiting-Raw-Table.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Recruiting-Raw-Table.png" alt="" width="624" height="321" class="aligncenter size-full wp-image-54700" srcset="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Recruiting-Raw-Table.png 624w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Recruiting-Raw-Table-300x154.png 300w" sizes="(max-width: 624px) 100vw, 624px" /></a></p>
<p>We haven't consolidated any records yet, but we can see the Category attributed to each record, and we can see that not every team has data in every position. Air Force, as seen above, has no data on Quarterback recruiting. There's some irony and a piloting joke in there, I'm sure of it.</p>
<p>In our next step, we introduce DuckDB functions, which match many of the push-down functions that <em>PROC SQL</em> can <a href="https://go.documentation.sas.com/doc/en/pgmsascdc/v_069/acreldb/n0ufjfisvdiw12n122hcamww5v05.htm">leverage in-database with different data sources</a>. We are using explicit passthrough here to leverage DuckDB's functions themselves but note that an <em>implicit passthrough</em> query could leverage many of these same functions.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Recruiting-Sums-Code.jpg"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Recruiting-Sums-Code.jpg" alt="" width="961" height="529" class="aligncenter size-full wp-image-54703" srcset="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Recruiting-Sums-Code.jpg 961w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Recruiting-Sums-Code-300x165.jpg 300w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Recruiting-Sums-Code-768x423.jpg 768w" sizes="(max-width: 961px) 100vw, 961px" /></a></p>
<p>We've now consolidated our data into a maximum of four records per team: Defense, Offense, Quarterback, and Special_Teams using the <em>sum()</em> functions in DuckDB, and leveraging our <em>GROUP BY</em> and <em>ORDER BY</em> statements to keep everything lined up nicely.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/DuckDb-Recruiting-Sums-Data.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/DuckDb-Recruiting-Sums-Data.png" alt="" width="624" height="411" class="aligncenter size-full wp-image-54706" srcset="https://blogs.sas.com/content/sgf/files/2025/12/DuckDb-Recruiting-Sums-Data.png 624w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDb-Recruiting-Sums-Data-300x198.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDb-Recruiting-Sums-Data-214x140.png 214w" sizes="(max-width: 624px) 100vw, 624px" /></a></p>
<p>With this consolidation done, we can introduce the highlight DuckDB feature of this article, something that <strong><em>can't be done</em></strong> using ANSI Standard <em>PROC SQL</em>: the <em>PIVOT</em> statement. <a href="https://duckdb.org/docs/stable/sql/statements/pivot">The PIVOT statement</a> in DuckDB, in conjunction with DuckDB's <em>coalesce()</em> function, allows us to restructure our data in one SQL statement, combining multiple records into multiple columns, grouped by the team.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Pivot-Code.jpg"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Pivot-Code.jpg" alt="" width="802" height="583" class="aligncenter size-full wp-image-54709" srcset="https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Pivot-Code.jpg 802w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Pivot-Code-300x218.jpg 300w, https://blogs.sas.com/content/sgf/files/2025/12/DuckDB-Pivot-Code-768x558.jpg 768w" sizes="(max-width: 802px) 100vw, 802px" /></a></p>
<p>Our newest table, <em>DUKLIB.RECRUITING_PIVOT</em>, now contains just a single record per team, with clearly named columns defining the number of commits, total rating, and (more importantly) average rating in each major category of the game.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/DUKLIBRECRUITING_PIVOT.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/DUKLIBRECRUITING_PIVOT.png" alt="" width="1412" height="604" class="aligncenter size-full wp-image-54772" srcset="https://blogs.sas.com/content/sgf/files/2025/12/DUKLIBRECRUITING_PIVOT.png 1412w, https://blogs.sas.com/content/sgf/files/2025/12/DUKLIBRECRUITING_PIVOT-300x128.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/DUKLIBRECRUITING_PIVOT-1024x438.png 1024w, https://blogs.sas.com/content/sgf/files/2025/12/DUKLIBRECRUITING_PIVOT-768x329.png 768w" sizes="(max-width: 1412px) 100vw, 1412px" /></a></p>
<p>The <em>PIVOT</em> statement is a powerful piece of logic that is now accessible to all SAS Viya programmers working with the SAS/ACCESS Engine to DuckDB. It's only one of many benefits that the newly introduced ACCESS Engine brings to the programmer experience, but it highlights SAS' paradigm of <strong>meeting your data where it is</strong>.</p>
<p>In my next article, I'll highlight more neat features that <em>PROC SQL</em> can take advantage of with DuckDB, and I'll engage a few more of our football datasets, updated for the end of the regular season, remarking along the way on how drastically the 2025 season's results have deviated from the expectations. In the meantime, you can learn more about <a href="https://go.documentation.sas.com/doc/en/pgmsascdc/v_069/acreldb/p1qjya05qkxd65n1j4pd7ln60lc7.htm">SAS/ACCESS to DuckDB in SAS Viya 4 here</a>.</p>
<p>Thanks for reading, and I'll see you in ten yards.</p>
<h2>Learn more</h2>
<ul>
<li><a href="https://www.sas.com/en_us/webinars/smarter-access-to-open-data.html">Smarter Access to Open Data: Introducing SAS/ACCESS to DuckDB</a></li>
<li><a href="https://communities.sas.com/t5/SAS-Communities-Library/Speeding-Up-Workloads-with-Ducks-Case-Study-of-New-DuckDB-ACCESS/ta-p/977090">Speeding Up Workloads with Ducks(?!): Case Study of New DuckDB ACCESS Engine</a></li>
<li><a href="https://communities.sas.com/t5/SAS-Communities-Library/The-Quack-is-Back-SAS-ACCESS-Meets-DuckDB/ta-p/969374">The Quack is Back: SAS/ACCESS Meets DuckDB</a></li>
</ul>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2025/12/10/touchduck/">TouchDuck! Exploring unique features of SAS/ACCESS to DuckDB through college football</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blogs.sas.com/content/sgf/2025/12/10/touchduck/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			<enclosure url="https://blogs.sas.com/content/sgf/files/2025/12/Score-150x150.png" />
	</item>
		<item>
		<title>Getting Started with Job Scheduling in SAS Viya</title>
		<link>https://blogs.sas.com/content/sgf/2025/12/04/getting-started-with-job-scheduling-in-sas-viya/</link>
		
		<dc:creator><![CDATA[Kevin Bickford]]></dc:creator>
		<pubDate>Thu, 04 Dec 2025 14:44:09 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[Job Execution]]></category>
		<category><![CDATA[Jobs and Flows]]></category>
		<category><![CDATA[SAS Viya]]></category>
		<guid isPermaLink="false">https://blogs.sas.com/content/sgf/?p=54286</guid>

					<description><![CDATA[<p>Getting Started with Job Scheduling in SAS Viya If you have ever had to manually trigger Viya jobs, you know the drill: it is tedious, and one forgotten click can throw everything off. That is where SAS Viya job scheduling comes in. It lets you automate your programs, data loads, [...]</p>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2025/12/04/getting-started-with-job-scheduling-in-sas-viya/">Getting Started with Job Scheduling in SAS Viya</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h1>Getting Started with Job Scheduling in SAS Viya</h1>
<p>If you have ever had to manually trigger Viya jobs, you know the drill: it is tedious, and one forgotten click can throw everything off.</p>
<p>That is where SAS Viya job scheduling comes in. It lets you automate your programs, data loads, reports, and analytics workflows so they run exactly when you need them, no babysitting required.</p>
<h2>What is a Job in Viya?</h2>
<p>Think of a job as any piece of SAS work you want to run, such as a program, a data plan, or even a Visual Analytics report refresh. Once it is saved in Viya (for example, from SAS Studio or Data Studio), you can schedule it to run automatically at a set time or on a recurring basis.</p>
<p>The first step is to write your code. In this example, I am using SAS Studio to create SAS code that will be submitted to Viya CAS for execution.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/createCode2.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/createCode2.png" alt="" width="582" height="253" class="aligncenter size-full wp-image-54307" srcset="https://blogs.sas.com/content/sgf/files/2025/12/createCode2.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/createCode2-300x130.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobexec2.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobexec2.png" alt="" width="576" height="279" class="aligncenter size-full wp-image-54514" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobexec2.png 576w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec2-300x145.png 300w" sizes="(max-width: 576px) 100vw, 576px" /></a></p>
<p>After saving your job in your Viya content folder, open the <strong>More</strong> menu (the three-dot icon). From the list of options, select <strong>Schedule as a Job</strong> to create a scheduled job from your code.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobexec25.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobexec25.png" alt="" width="582" height="270" class="aligncenter size-full wp-image-54592" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobexec25.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec25-300x139.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobexec3.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobexec3.png" alt="" width="214" height="316" class="aligncenter size-full wp-image-54517" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobexec3.png 214w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec3-203x300.png 203w" sizes="(max-width: 214px) 100vw, 214px" /></a></p>
<p>After selecting <strong>Schedule as a Job</strong>, the <strong>New Trigger</strong> dialog box appears. From this dialog, choose the <strong>Frequency</strong> (how often the job should run) and the <strong>Interval</strong> (how often that frequency occurs). </p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/newTrigger.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/newTrigger.png" alt="" width="582" height="255" class="aligncenter size-full wp-image-54325" srcset="https://blogs.sas.com/content/sgf/files/2025/12/newTrigger.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/newTrigger-300x131.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/newTrigger2.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/newTrigger2.png" alt="" width="582" height="444" class="aligncenter size-full wp-image-54328" srcset="https://blogs.sas.com/content/sgf/files/2025/12/newTrigger2.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/newTrigger2-300x229.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/newTrigger3.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/newTrigger3.png" alt="" width="576" height="427" class="aligncenter size-full wp-image-54331" srcset="https://blogs.sas.com/content/sgf/files/2025/12/newTrigger3.png 576w, https://blogs.sas.com/content/sgf/files/2025/12/newTrigger3-300x222.png 300w" sizes="(max-width: 576px) 100vw, 576px" /></a></p>
<p>Next, specify the <strong>Start Time</strong> for when you want the job to begin running.  </p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/newTrigger4.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/newTrigger4.png" alt="" width="582" height="449" class="aligncenter size-full wp-image-54334" srcset="https://blogs.sas.com/content/sgf/files/2025/12/newTrigger4.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/newTrigger4-300x231.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p>Select the appropriate <strong>Time Zone</strong>. </p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/timeZone.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/timeZone.png" alt="" width="582" height="433" class="aligncenter size-full wp-image-54337" srcset="https://blogs.sas.com/content/sgf/files/2025/12/timeZone.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/timeZone-300x223.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p>Then, set the <strong>Start Date</strong> for when the schedule should take effect. </p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/startDate.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/startDate.png" alt="" width="576" height="448" class="aligncenter size-full wp-image-54340" srcset="https://blogs.sas.com/content/sgf/files/2025/12/startDate.png 576w, https://blogs.sas.com/content/sgf/files/2025/12/startDate-300x233.png 300w" sizes="(max-width: 576px) 100vw, 576px" /></a></p>
<p>Finally, choose an <strong>End Date</strong> to define how long the job should remain scheduled.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/endDate.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/endDate.png" alt="" width="582" height="448" class="aligncenter size-full wp-image-54343" srcset="https://blogs.sas.com/content/sgf/files/2025/12/endDate.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/endDate-300x231.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p>To verify that your job has completed, open <strong>Environment Manager</strong> and navigate to the <strong>Jobs and Flows</strong> page. You should see your job listed there, and under the <strong>Scheduled</strong> column, a blue clock icon indicates that the job has an active schedule.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobexec4.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobexec4.png" alt="" width="582" height="270" class="aligncenter size-full wp-image-54520" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobexec4.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec4-300x139.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobexec5.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobexec5.png" alt="" width="576" height="301" class="aligncenter size-full wp-image-54526" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobexec5.png 576w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec5-300x157.png 300w" sizes="(max-width: 576px) 100vw, 576px" /></a></p>
<h2>Scheduling Made Simple</h2>
<p>SAS Viya’s Scheduling page (inside Environment Manager) is your main hub for automating jobs. From here, you can run a job immediately, set it to run daily, weekly, or yearly, and manage existing schedules — view, edit, disable, or delete them. You can even run a job as a different user (if you are an admin), which is handy when jobs need specific credentials. Currently, the only trigger type is time-based, so it is all about scheduling by the clock.<br />
Environment Manager is also where you will need to go to edit any jobs you have scheduled using visual tools (Visual Analytics, Visual Statistics, Visual Data Mining and Machine learning, Visual Forecasting and SAS Studio).</p>
<p><strong>The first step is to create a job from the SAS Code/Program you want to automate. Below are the steps to create a job in Environment Manager</strong></p>
<p>Navigate to Environment Manager by selecting the more menu from the Viya Welcome page.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobexec6.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobexec6.png" alt="" width="582" height="364" class="aligncenter size-full wp-image-54529" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobexec6.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec6-300x188.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec6-343x215.png 343w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p>Then select Manage Environment</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobexec24.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobexec24.png" alt="" width="582" height="361" class="aligncenter size-full wp-image-54586" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobexec24.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec24-300x186.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p>This is the Administrator’s view of Viya Environment Manager.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/envManager3.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/envManager3.png" alt="" width="582" height="264" class="aligncenter size-full wp-image-54355" srcset="https://blogs.sas.com/content/sgf/files/2025/12/envManager3.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/envManager3-300x136.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobexec8.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobexec8.png" alt="" width="229" height="559" class="aligncenter size-full wp-image-54535" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobexec8.png 229w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec8-123x300.png 123w" sizes="(max-width: 229px) 100vw, 229px" /></a></p>
<p>Navigate to the Jobs and Flows page. By default, the page will open in the Monitoring view. You will want to select the Scheduling View.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobexec9.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobexec9.png" alt="" width="582" height="297" class="aligncenter size-full wp-image-54538" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobexec9.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec9-300x153.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p>Select New -&gt; Job from the pulldown menu.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobexec10.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobexec10.png" alt="" width="574" height="318" class="aligncenter size-full wp-image-54541" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobexec10.png 574w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec10-300x166.png 300w" sizes="(max-width: 574px) 100vw, 574px" /></a></p>
<p>The New Job window will pop up. Fill in a Name you want give your Job.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/newJobName.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/newJobName.png" alt="" width="576" height="280" class="aligncenter size-full wp-image-54379" srcset="https://blogs.sas.com/content/sgf/files/2025/12/newJobName.png 576w, https://blogs.sas.com/content/sgf/files/2025/12/newJobName-300x146.png 300w" sizes="(max-width: 576px) 100vw, 576px" /></a></p>
<p>Select the location of the program you want to create a job for.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobexec11.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobexec11.png" alt="" width="576" height="280" class="aligncenter size-full wp-image-54544" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobexec11.png 576w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec11-300x146.png 300w" sizes="(max-width: 576px) 100vw, 576px" /></a></p>
<p>Navigate the folder of the Program you want to schedule.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobexec13.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobexec13.png" alt="" width="582" height="289" class="aligncenter size-full wp-image-54547" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobexec13.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec13-300x149.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec13-164x82.png 164w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p>Select OK.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobexec14.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobexec14.png" alt="" width="490" height="287" class="aligncenter size-full wp-image-54550" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobexec14.png 490w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec14-300x176.png 300w" sizes="(max-width: 490px) 100vw, 490px" /></a></p>
<p>Now that you have completed the job creation steps, you will want to schedule the job.</p>
<h2>How to Schedule a Job in SAS Viya</h2>
<p>Here is how it works in a nutshell:<br />
Open SAS Environment Manager and go to the <strong>Jobs and Flows</strong> page. Select the Schedule View, Select the job you want to automate, right Click on the Job and Select Schedule.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobexec15.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobexec15.png" alt="" width="582" height="309" class="aligncenter size-full wp-image-54553" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobexec15.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec15-300x159.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec15-351x185.png 351w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p>If you have Admin credentials and you want to run this scheduled job as another user, then select the icon on the “Run As” this will bring you to the list of available users. </p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobexec16.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobexec16.png" alt="" width="582" height="282" class="aligncenter size-full wp-image-54556" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobexec16.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec16-300x145.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p>Select the user’s name you want to schedule and run this job for. This will only work if you have admin credentials. Once you have selected the id then select OK.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/chooseUser.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/chooseUser.png" alt="" width="582" height="389" class="aligncenter size-full wp-image-54397" srcset="https://blogs.sas.com/content/sgf/files/2025/12/chooseUser.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/chooseUser-300x201.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p>Now you can pick a time to schedule your job for, select the + (plus sign).</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/testScheduleJob.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/testScheduleJob.png" alt="" width="582" height="465" class="aligncenter size-full wp-image-54403" srcset="https://blogs.sas.com/content/sgf/files/2025/12/testScheduleJob.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/testScheduleJob-300x240.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<ul>
<li>Name: Daily News Digest Trigger</li>
<li>
(A descriptive name for the time-based trigger to easily identify it in the scheduler.)</li>
<li>
Frequency: Daily</li>
<li>
(The job will run daily.)</li>
<li>
Interval: 1 Day</li>
<li>
(It occurs every 1 day, meaning once per day.)</li>
<li>
Run Time: 08:00 AM</li>
<li>
(The job starts at 8:00 AM local time for morning execution.)</li>
<li>
Time Zone: America/New York (Eastern Standard Time)</li>
<li>
(Suitable for US East Coast operations; adjust if targeting a different region.)</li>
<li>
Start Date:  </li>
<li>
End Date: you can select an exact date or never end the scheduled job.</li>
</ul>
<p>(Runs through the end date period; set to a far-future date like "Never" if indefinite.)</p>
<p>This setup would trigger the job every day at the time you selected as well as the time zone AM EST, starting tomorrow and will run every week until you cancel the job. When complete, select save. </p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobexec17.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobexec17.png" alt="" width="582" height="443" class="aligncenter size-full wp-image-54559" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobexec17.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec17-300x228.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p>To verify your job has been scheduled, locate your job in the list of Jobs and Flows and verify that there is a blue clock icon next to the job under the Scheduled column.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobexec18-1.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobexec18-1.png" alt="" width="582" height="284" class="aligncenter size-full wp-image-54601" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobexec18-1.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec18-1-300x146.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<h2>Monitoring Your Jobs</h2>
<p>Once your jobs are scheduled, you can track their progress right in the interface. View (visualize when jobs ran and whether they succeeded or failed). You can zoom in by time and even filter by job name or date range.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobexec26.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobexec26.png" alt="" width="582" height="271" class="aligncenter size-full wp-image-54607" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobexec26.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec26-300x140.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<h2>Going Beyond Jobs: What Are Flows?</h2>
<p>Sometimes when scheduling a job, a date and time trigger just is not enough. That is where job flows come in. A flow lets you connect multiple jobs together with conditions, so they run in sequence. For example, Job A loads data and Job B run analytics. Job B will only run after Job A finishes successfully. You can even build logic into your flows using AND /OR gates — it is like chaining tasks together into a smart, automated process.</p>
<p>Job flows are essential for enterprise-level scheduling, enabling reliable, repeatable automation of data processing pipelines, ETL (Extract, Transform, Load) operations, reporting, and more. They go beyond single job scheduling by handling interdependencies and error handling.</p>
<h2>Components of Job Flow</h2>
<p>A flow can include:</p>
<ul>
<li>Jobs (SAS programs, data plans, imports)</li>
<li>
Job Flows Dependencies and connections (nested flows)</li>
<li>
Logic Gates (AND, OR conditions)</li>
<li>
Time Events (scheduled triggers)</li>
<li>
Command Line Actions (bash scripts or external commands)</li>
</ul>
<p>Below is an example of a <strong>job flow</strong> created in the <em>Jobs and Flows</em> page of a <strong>Viya Environment Manager</strong>. This flow includes five jobs — a mix of SAS code, Data Explorer import tasks, and a Data Plan project — connected by two logic gates (one <strong>AND</strong> and one <strong>OR</strong>) and multiple connectors. The <strong>connectors</strong> (arrows) define job dependencies and determine the execution order. In this example, <strong>Jobs A, B, and C</strong> run at the same time when the flow starts. <strong>Job D</strong> runs only after <strong>Job C</strong> completes successfully, and <strong>Job E</strong> runs when <strong>Job D</strong> succeeds and either Job A or Job B finishes successfully.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobFlow.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobFlow.png" alt="" width="907" height="255" class="aligncenter size-full wp-image-54415" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobFlow.png 907w, https://blogs.sas.com/content/sgf/files/2025/12/jobFlow-300x84.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/jobFlow-768x216.png 768w" sizes="(max-width: 907px) 100vw, 907px" /></a></p>
<p>You can also include command-line action as part of a job flow. For example, this allows you to launch a SAS program in batch mode or run a Bash script directly from within the flow. Command-line actions can be saved to the Saved Actions area for reuse in other flows, making it easy to build on previous work. However, saved actions are user-specific and cannot be shared between different users.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/clActions.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/clActions.png" alt="" width="582" height="258" class="aligncenter size-full wp-image-54418" srcset="https://blogs.sas.com/content/sgf/files/2025/12/clActions.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/clActions-300x133.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p>This is an example of using the SAS Viya Command line as an Action in a Job Flow.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/clActions.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/clActions.png" alt="" width="582" height="256" class="aligncenter size-full wp-image-54421" /></a></p>
<p>This is an example of using the SAS Viya Command line as an Action in a Job Flow.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/clActions2.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/clActions2.png" alt="" width="582" height="256" class="aligncenter size-full wp-image-54421" srcset="https://blogs.sas.com/content/sgf/files/2025/12/clActions2.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/clActions2-300x132.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p>This is an example of using A Lunix Bash command line as an Action in a Job Flow.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/clActions3.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/clActions3.png" alt="" width="582" height="264" class="aligncenter size-full wp-image-54424" srcset="https://blogs.sas.com/content/sgf/files/2025/12/clActions3.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/clActions3-300x136.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<h2>Creating a Job Flow </h2>
<p>In Environment Manager navigate to the Jobs and Flow page, you will want to be in the Scheduling View. Just like when we create a new Job, we want to select New and from the list select <strong><em>Job Flow</em></strong>.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobexec19.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobexec19.png" alt="" width="582" height="325" class="aligncenter size-full wp-image-54565" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobexec19.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec19-300x168.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p>In the New Job Flow window, select objects that you want to add to the flow in the tree view and drag them to the flow editor.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/flowEditor.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/flowEditor.png" alt="" width="582" height="285" class="aligncenter size-full wp-image-54430" srcset="https://blogs.sas.com/content/sgf/files/2025/12/flowEditor.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/flowEditor-300x147.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p>In the New Job Flow window, select objects that you want to add to the flow in the tree view and drag them to the flow editor.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobexec20.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobexec20.png" alt="" width="582" height="285" class="aligncenter size-full wp-image-54568" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobexec20.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec20-300x147.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p>As you add objects to the flow, make connections between the objects to specify the sequence of the flow. </p>
<p>Click on the right side of the first object in the order and drag a line to the right side of the next object in the order. </p>
<p>Each object (other than gates) can have only one input connection and one output connection. </p>
<p>Objects change positions in the window automatically as you make connections.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/flowEditor3.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/flowEditor3.png" alt="" width="576" height="277" class="aligncenter size-full wp-image-54436" srcset="https://blogs.sas.com/content/sgf/files/2025/12/flowEditor3.png 576w, https://blogs.sas.com/content/sgf/files/2025/12/flowEditor3-300x144.png 300w" sizes="(max-width: 576px) 100vw, 576px" /></a></p>
<p>Adding an And Gate.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/flowEditor4.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/flowEditor4.png" alt="" width="595" height="290" class="aligncenter size-full wp-image-54439" srcset="https://blogs.sas.com/content/sgf/files/2025/12/flowEditor4.png 595w, https://blogs.sas.com/content/sgf/files/2025/12/flowEditor4-300x146.png 300w" sizes="(max-width: 595px) 100vw, 595px" /></a></p>
<p>Connect them in the order you want each job to run.<a href="https://blogs.sas.com/content/sgf/files/2025/12/orderRun.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/orderRun.png" alt="" width="582" height="286" class="aligncenter size-full wp-image-54442" srcset="https://blogs.sas.com/content/sgf/files/2025/12/orderRun.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/orderRun-300x147.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/orderRun-164x82.png 164w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p>In this example, this is the order the jobs need to run; the first two jobs will be executed first and if they both run successfully then the last job will run.<a href="https://blogs.sas.com/content/sgf/files/2025/12/orderRun2.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/orderRun2.png" alt="" width="582" height="254" class="aligncenter size-full wp-image-54445" srcset="https://blogs.sas.com/content/sgf/files/2025/12/orderRun2.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/orderRun2-300x131.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/orderRun3.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/orderRun3.png" alt="" width="582" height="282" class="aligncenter size-full wp-image-54448" srcset="https://blogs.sas.com/content/sgf/files/2025/12/orderRun3.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/orderRun3-300x145.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p>In this example, you can see additional options available in the right-hand menu. The options displayed depend on the section of the flow that you select, allowing you to customize and control various aspects of the flow’s functionality.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/orderRun4.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/orderRun4.png" alt="" width="582" height="262" class="aligncenter size-full wp-image-54451" srcset="https://blogs.sas.com/content/sgf/files/2025/12/orderRun4.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/orderRun4-300x135.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobexec21.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobexec21.png" alt="" width="582" height="284" class="aligncenter size-full wp-image-54574" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobexec21.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec21-300x146.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/newJobFlow3.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/newJobFlow3.png" alt="" width="576" height="276" class="aligncenter size-full wp-image-54460" srcset="https://blogs.sas.com/content/sgf/files/2025/12/newJobFlow3.png 576w, https://blogs.sas.com/content/sgf/files/2025/12/newJobFlow3-300x144.png 300w" sizes="(max-width: 576px) 100vw, 576px" /></a></p>
<p>You can see additional options available on the <strong>right-hand menu</strong>. Once you have made all your selections you can save your flow.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobexec22.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobexec22.png" alt="" width="582" height="252" class="aligncenter size-full wp-image-54577" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobexec22.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec22-300x130.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/newJobFlow5.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/newJobFlow5.png" alt="" width="582" height="286" class="aligncenter size-full wp-image-54469" srcset="https://blogs.sas.com/content/sgf/files/2025/12/newJobFlow5.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/newJobFlow5-300x147.png 300w, https://blogs.sas.com/content/sgf/files/2025/12/newJobFlow5-164x82.png 164w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p>Once Saved, you can now schedule your flow jut like you scheduled your job.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/jobexec23.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/jobexec23.png" alt="" width="582" height="267" class="aligncenter size-full wp-image-54580" srcset="https://blogs.sas.com/content/sgf/files/2025/12/jobexec23.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/jobexec23-300x138.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/12/scheduleJob7.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/12/scheduleJob7.png" alt="" width="582" height="316" class="aligncenter size-full wp-image-54475" srcset="https://blogs.sas.com/content/sgf/files/2025/12/scheduleJob7.png 582w, https://blogs.sas.com/content/sgf/files/2025/12/scheduleJob7-300x163.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<p>For more information on Scheduling in Viya please visit this link <a href="https://documentation.sas.com/?cdcId=sasadmincdc&amp;cdcVersion=default&amp;docsetId=caljobs&amp;docsetTarget=titlepage.htm">SAS Help Center: SAS Viya Platform: Jobs and Flows</a>.</p>
<h2>Command Line Scheduling (For Power Users)</h2>
<p>Prefer the command line? SAS Viya lets you run and schedule jobs using the CLI (Command Line Interface). You can create the same time-based triggers — down to minutes, hours, days, weeks, or months — right from your terminal. This is great for admins who like automation scripts or want to integrate Viya jobs into broader IT workflows.</p>
<p>For more information on scheduling Viya jobs using the command line click here <a href="https://documentation.sas.com/?cdcId=sasadmincdc&amp;cdcVersion=default&amp;docsetId=caljobs&amp;docsetTarget=p0cblrmnt12widn13p0wm385r5q1.htm">SAS Help Center: Jobs and Flows: How To (CLI)</a>.</p>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2025/12/04/getting-started-with-job-scheduling-in-sas-viya/">Getting Started with Job Scheduling in SAS Viya</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></content:encoded>
					
		
		
			<enclosure url="https://blogs.sas.com/content/sgf/files/2025/12/newJob2-150x150.png" />
	</item>
		<item>
		<title>SAS Viya Workload Management</title>
		<link>https://blogs.sas.com/content/sgf/2025/11/12/sas-viya-workload-management/</link>
		
		<dc:creator><![CDATA[Kevin Bickford]]></dc:creator>
		<pubDate>Wed, 12 Nov 2025 17:13:33 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[data management]]></category>
		<category><![CDATA[SAS Administrators]]></category>
		<guid isPermaLink="false">https://blogs.sas.com/content/sgf/?p=54166</guid>

					<description><![CDATA[<p>Managing workloads in modern analytics environments is not keeping systems running, it’s about making sure the right jobs get the right resources at the right time. As organizations move analytics to the cloud, powered by Kubernetes, balancing workloads across computer resources becomes a critical challenge.</p>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2025/11/12/sas-viya-workload-management/">SAS Viya Workload Management</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h1>What is SAS Viya with Workload Management</h1>
<p>Managing workloads in modern analytics environments is not keeping systems running, it’s about making sure the right jobs get the right resources at the right time. As organizations move analytics to the cloud, powered by Kubernetes, balancing workloads across computer resources becomes a critical challenge.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/11/kubernetesSASViya.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-54181" src="https://blogs.sas.com/content/sgf/files/2025/11/kubernetesSASViya.png" alt="" width="751" height="214" srcset="https://blogs.sas.com/content/sgf/files/2025/11/kubernetesSASViya.png 751w, https://blogs.sas.com/content/sgf/files/2025/11/kubernetesSASViya-300x85.png 300w" sizes="(max-width: 751px) 100vw, 751px" /></a></p>
<p>That’s where SAS Viya Workload Management (WLM) comes in. By extending Kubernetes’ native scheduling and workload orchestration, SAS Workload Management ensures jobs run efficiently, users get predictable performance, and administrators maintain visibility and control.</p>
<p>This post, will break down what SAS Workload Management is, how it works, and why it’s a game-changer for SAS Viya environments.</p>
<h2>What is SAS Workload Management?</h2>
<p>SAS Workload Management is a framework that distributes and balances SAS computing tasks across Kubernetes clusters. It builds on Kubernetes’ workload orchestration but adds intelligence for SAS use cases.</p>
<p>With Workload Management, administrators can:</p>
<ul>
<li>Define job priorities by user, group, or workload type.</li>
<li>Optimize compute resource usage.</li>
<li>Prevent system overload.</li>
<li>Ensure that critical work gets done quickly.</li>
</ul>
<p>The result is a smarter, policy-driven approach to managing complex analytics environments.</p>
<h2>Key Features of SAS Workload Management</h2>
<ol>
<li><strong>Centralized Management</strong>: Administrators can centrally define policies that align with business goals. For example, critical users or workloads can be prioritized to ensure they always receive the resources they need. Policies also extend monitoring capabilities beyond Kubernetes admins—SAS administrators and even end-users can view and track their own jobs.</li>
<li><strong>Multi-User Workload Balancing:</strong> In multi-user environments, job distribution is crucial. SAS Workload Management automatically directs jobs to the best available host, preventing bottlenecks. By avoiding situations where too many jobs run on a single node, the system ensures timely job completion across all users.</li>
<li><strong>Parallelized Workload Execution:</strong> Traditional SAS programs are executed line by line. However, many steps in workflow are independent and can be executed simultaneously. With Workload Management, independent steps can be run as separate jobs in parallel, significantly reducing execution time compared to serial processing.</li>
<li><strong>Enterprise Scheduling:</strong> While Kubernetes offers basic scheduling, SAS Workload Management introduces policy-driven scheduling. Jobs are not just queued, they’re executed on the most appropriate host, increasing the likelihood of on-time completion.</li>
<li><strong>High Availability &amp; Scalability:</strong> SAS Workload Management leverages Kubernetes to provide resiliency and elasticity. Jobs can automatically restart from their last checkpoint, ensuring business continuity even after interruptions. Additionally, clusters can scale up or down dynamically based on demand.</li>
<li><strong>Monitoring &amp; Administration:</strong> Administrators aren’t left in the dark. Through the SAS Environment Manager and dashboards in Grafana, SAS Workload Management offers detailed monitoring of jobs, queues, and hosts. Unlike Kubernetes’ general metrics, these dashboards deliver SAS-specific insights.</li>
</ol>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/11/wmBenefits.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-54196" src="https://blogs.sas.com/content/sgf/files/2025/11/wmBenefits.png" alt="" width="735" height="392" srcset="https://blogs.sas.com/content/sgf/files/2025/11/wmBenefits.png 735w, https://blogs.sas.com/content/sgf/files/2025/11/wmBenefits-300x160.png 300w" sizes="(max-width: 735px) 100vw, 735px" /></a></p>
<h2>Administration Tools</h2>
<p>Administrators can interact with SAS Workload Management through two main tools:</p>
<ol>
<li><strong>SAS Environment Manager:</strong> Provides a graphical interface to monitor and manage jobs, queues, and hosts. Both administrators and users can access dashboards tailored to their roles. <a href="https://www.sas.com/en_us/software/workload-management.html">SAS Workload Management | SAS</a></li>
<li><strong>Command Line Interface (CLI)</strong>: Offers flexibility for automation and scripting. Administrators can use CLI plugins to manage workloads remotely, making it easy to integrate into existing DevOps processes. <a href="https://go.documentation.sas.com/api/docsets/calcli/v_001/content/calcli.pdf?locale=en">SAS Viya Platform: Using the Command-Line Interface</a></li>
</ol>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/11/wmAdminTools.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-54199" src="https://blogs.sas.com/content/sgf/files/2025/11/wmAdminTools.png" alt="" width="646" height="343" srcset="https://blogs.sas.com/content/sgf/files/2025/11/wmAdminTools.png 646w, https://blogs.sas.com/content/sgf/files/2025/11/wmAdminTools-300x159.png 300w, https://blogs.sas.com/content/sgf/files/2025/11/wmAdminTools-351x185.png 351w" sizes="(max-width: 646px) 100vw, 646px" /></a></p>
<h2>Components of SAS Workload Management</h2>
<p>These are the key components:</p>
<ul>
<li><strong>Workload Orchestrator:</strong> The central brain that manages, monitors, and collects data on jobs and hosts.</li>
<li><strong>Jobs:</strong> Units of work submitted by users. Each job has states like RUNNING or COMPLETED and runs in its own Kubernetes pod.</li>
<li><strong>Queues:</strong> Containers where jobs wait before being dispatched. Queues are governed by policies and priorities.</li>
<li><strong>Policies:</strong> Rules that control how workloads are dispatched, ensuring resources are allocated fairly and strategically.</li>
<li><strong>Hosts:</strong> Kubernetes nodes where jobs run. Each host has limits on how many jobs it can process concurrently.</li>
</ul>
<p>Together, these components create a dynamic, policy-driven environment for managing SAS workloads.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/11/wmComponents.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-54208" src="https://blogs.sas.com/content/sgf/files/2025/11/wmComponents.png" alt="" width="840" height="456" srcset="https://blogs.sas.com/content/sgf/files/2025/11/wmComponents.png 840w, https://blogs.sas.com/content/sgf/files/2025/11/wmComponents-300x163.png 300w, https://blogs.sas.com/content/sgf/files/2025/11/wmComponents-768x417.png 768w" sizes="(max-width: 840px) 100vw, 840px" /></a></p>
<h3>Workload Manager Dashboard page</h3>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/11/wmDashboard.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-54211" src="https://blogs.sas.com/content/sgf/files/2025/11/wmDashboard.png" alt="" width="593" height="270" srcset="https://blogs.sas.com/content/sgf/files/2025/11/wmDashboard.png 593w, https://blogs.sas.com/content/sgf/files/2025/11/wmDashboard-300x137.png 300w" sizes="(max-width: 593px) 100vw, 593px" /></a></p>
<h3>Workload Manager Jobs page</h3>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/11/wmJobs.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-54214" src="https://blogs.sas.com/content/sgf/files/2025/11/wmJobs.png" alt="" width="607" height="272" srcset="https://blogs.sas.com/content/sgf/files/2025/11/wmJobs.png 607w, https://blogs.sas.com/content/sgf/files/2025/11/wmJobs-300x134.png 300w" sizes="(max-width: 607px) 100vw, 607px" /></a></p>
<h3>Workload Manager Queue Page</h3>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/11/wmQueues.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-54223" src="https://blogs.sas.com/content/sgf/files/2025/11/wmQueues.png" alt="" width="582" height="267" srcset="https://blogs.sas.com/content/sgf/files/2025/11/wmQueues.png 582w, https://blogs.sas.com/content/sgf/files/2025/11/wmQueues-300x138.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<h3>Workload Manager Host Page</h3>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/11/wmHost.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-54226" src="https://blogs.sas.com/content/sgf/files/2025/11/wmHost.png" alt="" width="582" height="261" srcset="https://blogs.sas.com/content/sgf/files/2025/11/wmHost.png 582w, https://blogs.sas.com/content/sgf/files/2025/11/wmHost-300x135.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<h3>Workload Manager Logs page</h3>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/11/wmLogs.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-54229" src="https://blogs.sas.com/content/sgf/files/2025/11/wmLogs.png" alt="" width="582" height="262" srcset="https://blogs.sas.com/content/sgf/files/2025/11/wmLogs.png 582w, https://blogs.sas.com/content/sgf/files/2025/11/wmLogs-300x135.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<h3>Workload Manager Configuration page</h3>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/11/wmConfiguration.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-54232" src="https://blogs.sas.com/content/sgf/files/2025/11/wmConfiguration.png" alt="" width="582" height="260" srcset="https://blogs.sas.com/content/sgf/files/2025/11/wmConfiguration.png 582w, https://blogs.sas.com/content/sgf/files/2025/11/wmConfiguration-300x134.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<h3>Workload Manager Log Levels page</h3>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/11/wmLogLevels.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-54235" src="https://blogs.sas.com/content/sgf/files/2025/11/wmLogLevels.png" alt="" width="582" height="261" srcset="https://blogs.sas.com/content/sgf/files/2025/11/wmLogLevels.png 582w, https://blogs.sas.com/content/sgf/files/2025/11/wmLogLevels-300x135.png 300w" sizes="(max-width: 582px) 100vw, 582px" /></a></p>
<h2>How SAS Workload Management Works.</h2>
<p>Here is what happens behind the scenes when a SAS job is submitted:</p>
<ol>
<li>A user submits a job through a service like SAS Studio.</li>
<li>The job is sent to a queue, where it is prioritized relative to other jobs.</li>
<li>The Workload Orchestrator evaluates available Kubernetes nodes and selectsthe best host based on resources and policies.</li>
<li>The job is dispatched to the chosen host and executed in a pod.</li>
<li>If resources are unavailable, the job waits or in some cases, a lower-priority job may be preempted.</li>
<li>Once complete, the pod is shut down, freeing resources for future jobs.</li>
</ol>
<h2>Flow of how Workload Manager works:</h2>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/11/wmFlow.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-54238" src="https://blogs.sas.com/content/sgf/files/2025/11/wmFlow.png" alt="" width="755" height="445" srcset="https://blogs.sas.com/content/sgf/files/2025/11/wmFlow.png 755w, https://blogs.sas.com/content/sgf/files/2025/11/wmFlow-300x177.png 300w" sizes="(max-width: 755px) 100vw, 755px" /></a></p>
<h3>Job Lifecycle</h3>
<p>This is an example of the life cycle of a Viya Job once it is submitted to Workload manager.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/11/wmLifeCycle.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-54241" src="https://blogs.sas.com/content/sgf/files/2025/11/wmLifeCycle.png" alt="" width="491" height="225" srcset="https://blogs.sas.com/content/sgf/files/2025/11/wmLifeCycle.png 491w, https://blogs.sas.com/content/sgf/files/2025/11/wmLifeCycle-300x137.png 300w" sizes="(max-width: 491px) 100vw, 491px" /></a></p>
<h3>High-level flow</h3>
<p>This is an example of a Viya Job flow once it is submitted to Workload manager.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/11/wmJobFlow-1.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-54247" src="https://blogs.sas.com/content/sgf/files/2025/11/wmJobFlow-1.png" alt="" width="486" height="206" srcset="https://blogs.sas.com/content/sgf/files/2025/11/wmJobFlow-1.png 486w, https://blogs.sas.com/content/sgf/files/2025/11/wmJobFlow-1-300x127.png 300w" sizes="(max-width: 486px) 100vw, 486px" /></a></p>
<h2>SAS Viya Workload Management vs. SAS 9 Grid Manager</h2>
<p>If you have SAS 9.4 Grid Manager, this may all sound familiar. SAS 9.4 Grid Manager and Viya Workload Manager share the same fundamental mission, to orchestrate, balance and prioritize SAS Jobs across SAS computing resources.</p>
<p>The main difference is that SAS 9.4 Grid Manager was built for traditional on premises clusters, while Viya 4 Workload Manager is Kubernetes-native, making it more flexible for cloud and hybrid deployments.</p>
<p>For organizations migrating from SAS 9 Grid Manager, Workload Management in SAS Viya will feel familiar but more modern.</p>
<ul>
<li>Both systems balance workloads and manage resources.</li>
<li>SAS Viya WLM leverages Kubernetes, making it more flexible and scalable in cloud-native environments.</li>
<li>The interface and functionality are designed to ease migration, maintaining a sense of continuity for administrators.</li>
</ul>
<p>In short, SAS Viya Workload Management is the next step in workload orchestration—built for today’s hybrid and cloud-native analytics environments.</p>
<p>Below is a table for comparison between SAS 9.4 Grid Manager and SAS Viya 4 Workload Manager.</p>
<div class="row cf"><div class="column one-fourth"><strong>Category</strong></div><div class="column one-fourth"><strong>SAS 9.4 Grid Manager</strong></div><div class="column one-fourth"><strong>SAS Viya 4 Workload Management</strong></div><div class="column one-fourth"><strong>Similarity / Difference</strong></div></div>
<div class="row cf"><div class="column one-fourth"><strong>Core Purpose</strong></div><div class="column one-fourth">Distributes SAS jobs across a cluster of servers</div><div class="column one-fourth">Distributes SAS jobs across Kubernetes nodes</div><div class="column one-fourth"><img src="https://s.w.org/images/core/emoji/15.0.3/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Similar – both provide workload distribution</div></div>
<div class="row cf"><div class="column one-fourth"><strong>Architecture</strong></div><div class="column one-fourth">Built on traditional cluster/grid infrastructure</div><div class="column one-fourth">Cloud-native, Kubernetes-based orchestration</div><div class="column one-fourth"><img src="https://s.w.org/images/core/emoji/15.0.3/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Difference</div></div>
<div class="row cf"><div class="column one-fourth"><strong>Job Distribution</strong></div><div class="column one-fourth">Uses LSF (Load Sharing Facility) or 9.4 (SWO) schedulers to distribute jobs</div><div class="column one-fourth">Uses SAS Workload Orchestrator (SWO) integrated with Kubernetes</div><div class="column one-fourth"><img src="https://s.w.org/images/core/emoji/15.0.3/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Similar in concept, <img src="https://s.w.org/images/core/emoji/15.0.3/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> different implementation</div></div>
<div class="row cf"><div class="column one-fourth"><strong>Queues &amp; Policies</strong></div><div class="column one-fourth">Jobs submitted to queues, policies define priorities</div><div class="column one-fourth">Jobs submitted to queues, policies define priorities</div><div class="column one-fourth"><img src="https://s.w.org/images/core/emoji/15.0.3/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Similar</div></div>
<div class="row cf"><div class="column one-fourth"><strong>Administration Tools</strong></div><div class="column one-fourth">SAS Management Console (Grid Manager plug-in)</div><div class="column one-fourth">SAS Environment Manager, CLI, Grafana dashboards</div><div class="column one-fourth"><img src="https://s.w.org/images/core/emoji/15.0.3/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Difference</div></div>
<div class="row cf"><div class="column one-fourth"><strong>Monitoring</strong></div><div class="column one-fourth">Admins monitor jobs, queues, and nodes via Grid tools</div><div class="column one-fourth">Admins &amp; users monitor jobs, queues, and hosts with Environment Manager</div><div class="column one-fourth"><img src="https://s.w.org/images/core/emoji/15.0.3/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Similar, <img src="https://s.w.org/images/core/emoji/15.0.3/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> difference in tooling</div></div>
<div class="row cf"><div class="column one-fourth"><b>User Access</b></div><div class="column one-fourth">Centralized control, mostly admin-focused</div><div class="column one-fourth">Broader monitoring: admins, queue admins, end-users can view their own jobs</div><div class="column one-fourth"><img src="https://s.w.org/images/core/emoji/15.0.3/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Similar but Viya is more user-friendly</div></div>
<div class="row cf"><div class="column one-fourth"><strong>Parallelization</strong></div><div class="column one-fourth">Supports splitting jobs into multiple sub-tasks for parallel execution</div><div class="column one-fourth">Supports parallelized workloads (independent steps/jobs run in separate pods)</div><div class="column one-fourth"><img src="https://s.w.org/images/core/emoji/15.0.3/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Similar</div></div>
<div class="row cf"><div class="column one-fourth"><strong>High Availability</strong></div><div class="column one-fourth">Provides job recovery/failover in case of node failure</div><div class="column one-fourth">Kubernetes-native HA: restarts jobs, Auto scales cluster, ensures continuity</div><div class="column one-fourth"><img src="https://s.w.org/images/core/emoji/15.0.3/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Similar, <img src="https://s.w.org/images/core/emoji/15.0.3/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Viya has stronger cloud-native resiliency</div></div>
<div class="row cf"><div class="column one-fourth"><strong>Scalability</strong></div><div class="column one-fourth">Limited by physical cluster resources</div><div class="column one-fourth">Kubernetes allows elastic scaling (horizontal/vertical)</div><div class="column one-fourth"><img src="https://s.w.org/images/core/emoji/15.0.3/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Difference</div></div>
<div class="row cf"><div class="column one-fourth"><strong>Deployment Model</strong></div><div class="column one-fourth">Primarily on-premises clusters</div><div class="column one-fourth">Cloud, hybrid, or on-premises with Kubernetes</div><div class="column one-fourth"><img src="https://s.w.org/images/core/emoji/15.0.3/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Difference</div></div>
<div class="row cf"><div class="column one-fourth"><strong>Target Users</strong></div><div class="column one-fourth">Enterprises running SAS 9 workloads on grid</div><div class="column one-fourth">Enterprises moving to SAS Viya cloud-native analytics</div><div class="column one-fourth"><img src="https://s.w.org/images/core/emoji/15.0.3/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Similar audience, <img src="https://s.w.org/images/core/emoji/15.0.3/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> different platform maturity</div></div>
<h2>CAS vs Workload Manager</h2>
<p>SAS Viya CAS (Cloud Analytic Service) Think of CAS as a turbo engine bult to crunch huge datasets quickly using in-memory computing. When you use Viya Visual tools such as Visual Analytics, Model Studio, or CAS actions, it takes advantage of CAS.</p>
<p>CAS is the Viya engine where heavy analytics work happens.</p>
<ul>
<li>CAS loads your data into memory so it can process your jobs fast.</li>
<li>
It runs calculations, models, and reports.</li>
<li>
The more workers nodes you have, the faster it can run your jobs.</li>
</ul>
<p>Workload Manager is like a traffic controller or project manager for SAS Jobs that takes advantage of the Compute processing using traditional CPU-based SAS code job execution.</p>
<ul>
<li>It decides which jobs run first, and which ones wait.</li>
<li>
It makes sure no single user or job hogs the system.</li>
<li>
It manages queues, priorities, and resources so everything runs smoothly.</li>
<li>
It is built on Kubernetes, which means it can scale up or down automatically in the cloud.</li>
</ul>
<h2>Real-World Benefits</h2>
<p>Implementing SAS Workload Management translates into measurable improvements:  </p>
<ul>
<li><strong>Efficiency:</strong> Resources are used optimally, reducing wasted capacity. </li>
<li><strong><br />
Performance:</strong> Critical jobs finish faster with prioritization and parallel execution. </li>
<li>
<strong>Transparency:</strong> Administrators and users alike can monitor workloads in real time. </li>
<li>
<strong>Resiliency:</strong> Built-in high availability keeps business processes running smoothly. </li>
</ul>
<h2>Conclusion  </h2>
<p>As analytics environments grow in complexity, managing workloads effectively is no longer optional—it’s essential. SAS Viya Workload Management provides the tools and intelligence needed to ensure that jobs are prioritized, resources are optimized, and business-critical tasks are completed without interruption. </p>
<p>Whether you’re migrating from SAS 9 Grid Manager or starting fresh with SAS Viya, Workload Management offers a future-ready solution to workload orchestration. </p>
<p>Since SAS Viya Workload Manager is now standard with Viya 4, now is the time to explore how Viya 4 Workload Management can help you get more value out of your analytics environment.</p>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2025/11/12/sas-viya-workload-management/">SAS Viya Workload Management</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></content:encoded>
					
		
		
			<enclosure url="https://blogs.sas.com/content/sgf/files/2025/11/wmBenefits-150x150.png" />
	</item>
		<item>
		<title>Quickly and easily register Python models in SAS Viya Workbench</title>
		<link>https://blogs.sas.com/content/sgf/2025/09/30/register-python-models-in-sas-viya-workbench/</link>
		
		<dc:creator><![CDATA[Stu Sztukowski]]></dc:creator>
		<pubDate>Tue, 30 Sep 2025 13:53:14 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[machine learning deployment]]></category>
		<category><![CDATA[ModelOps]]></category>
		<category><![CDATA[Python model registration]]></category>
		<category><![CDATA[pzmm]]></category>
		<category><![CDATA[sas model manager]]></category>
		<category><![CDATA[SAS Viya Workbench]]></category>
		<category><![CDATA[sasctl]]></category>
		<category><![CDATA[XGBoost]]></category>
		<guid isPermaLink="false">https://blogs.sas.com/content/sgf/?p=54023</guid>

					<description><![CDATA[<p>Learn how to seamlessly register and deploy Python models (specifically an XGBoost classifier) into SAS Model Manager using SAS Viya Workbench and the pzmm package, enabling efficient ModelOps integration and production readiness.</p>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2025/09/30/register-python-models-in-sas-viya-workbench/">Quickly and easily register Python models in SAS Viya Workbench</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>It’s done. It’s finally done. After many caffeine-fueled late nights and countless hours of feature engineering, you’ve built the ultimate model. But the journey isn’t over. Not quite yet. There’s one thing left for you to do: get that model into production and let your ModelOps team take the reins. That’s where SAS Model Manager shines. While there are many ways to deploy a model built in SAS Viya Workbench (even if you have no other SAS products), using SAS Model Manager to deploy a model within a SAS Viya environment is an excellent option and <a href="https://www.sas.com/en_us/software/model-manager/features-list.html">has many benefits</a>. In this article, we’ll do a deep dive into how to do that.</p>
<p>When you’re in SAS Viya Workbench, you have access to powerful computer environments for complex experimentation that your thin client laptop just can’t handle. But that doesn’t mean it lives on its own isolated island. Once your model is ready to go, you need a bridge to SAS Viya to get it into SAS Model Manager. For SAS models, that’s <a href="https://go.documentation.sas.com/doc/en/workbenchcdc/v_001/vwbcasml/vwbcasml_registermodel_gettingstarted.htm">PROC REGISTERMODEL</a>. For open-source models, that’s <a href="https://sassoftware.github.io/python-sasctl">sasctl</a>, SAS’s open-source package with flavors in both Python and <a href="https://github.com/sassoftware/r-sasctl">R</a> designed to handle everything you need to register your model with SAS Viya.</p>
<p>With all the tools we have in our arsenal, let’s walk through a quick example of building, registering, and testing an XGBoost model that predicts loan defaults in the classic <a href="https://support.sas.com/documentation/onlinedoc/viya/examples.htm">HMEQ dataset</a>.</p>
<h1>Hello, I’m SAS Viya Workbench. Can you hear me?</h1>
<p>If you’ve ever registered a Python model from your own machine into SAS Model Manager, you’ll be happy to know that <strong>there’s no difference with SAS Viya Workbench</strong>. The same code that you would use on your local machine works the same way in SAS Viya Workbench. All you need to do is make sure that SAS Viya Workbench can talk with your SAS Viya Server.</p>
<p>The first thing you need to do is run a quick test. Run the code snippet below:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> requests
&nbsp;
host <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'https://my-viya-server.com'</span>
&nbsp;
resp <span style="color: #66cc66;">=</span> requests.<span style="color: black;">get</span><span style="color: black;">&#40;</span>f<span style="color: #483d8b;">'{host}/SASLogon'</span><span style="color: #66cc66;">,</span> verify<span style="color: #66cc66;">=</span><span style="color: #008000;">False</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> resp.<span style="color: black;">status_code</span> <span style="color: #66cc66;">==</span> <span style="color: #ff4500;">200</span>:
    <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Status: 200. You can successfully communicate with the server.'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">else</span>:
    <span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;WARNING: Received a non-200 status code:&quot;</span><span style="color: #66cc66;">,</span> resp.<span style="color: black;">status_code</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>If you receive no connection errors and a 200-status code, you’re all set: SAS Viya Workbench can talk with your SAS Viya server. If not, ask your administrator to allow a connection from SAS Viya Workbench to your SAS Viya server. And if all else fails, you still have the option to download your model as a zip file and upload it to SAS Model Manager – we’ll talk about that later.</p>
<p>If you’re already up to speed on exactly how to register a Python model to SAS Model Manager, then you can stop here. Seriously: that’s how seamless SAS Viya Workbench is. But if you’d like a refresher, a bit more explanation, and <a href="https://github.com/stu-code/register-xgboost-model/tree/main">a notebook you can run yourself</a>, then keep on reading.</p>
<h1>Let’s register</h1>
<p>We are going to use the <a href="https://sassoftware.github.io/python-sasctl/api/sasctl.pzmm.html">pzmm (Python Zip Model Management)</a> package from <a href="https://sassoftware.github.io/python-sasctl">sasctl</a> to do the heavy lifting. SAS Model Manager expects certain files and score code. You can technically write all of them yourself, but it’s much easier to let pzmm do it for you. Let’s set the stage with the model we’ll be registering.</p>
<h2>The model</h2>
<p>I’m going to intentionally skip past the model building details and get straight to what you’re here for: what is it that I need to do to register my Python model to SAS Viya? Just so you have a bit more context, here’s everything you need to know:</p>
<ul>
<li>Our data is <a href="https://support.sas.com/documentation/onlinedoc/viya/examples.htm">HMEQ</a></li>
<li>We’re predicting bad loans using the column "BAD"</li>
<li>We’ve built an <a href="https://xgboost.readthedocs.io/en/stable/">XGBoost</a> model with early stopping based on validation data</li>
<li>The model is:</li>
</ul>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">xgb_model <span style="color: #66cc66;">=</span> xgb.<span style="color: black;">XGBClassifier</span><span style="color: black;">&#40;</span>
    objective<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;binary:logistic&quot;</span><span style="color: #66cc66;">,</span>
    random_state<span style="color: #66cc66;">=</span><span style="color: #ff4500;">42</span><span style="color: #66cc66;">,</span>
    n_estimators<span style="color: #66cc66;">=</span>xgb_eval.<span style="color: black;">get_booster</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">best_iteration</span> + <span style="color: #ff4500;">1</span>
<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Now that you’ve got the background, let’s talk turkey.</p>
<h2>It’s all about the prep work</h2>
<p>SAS Model Manager requires specific information about your model that pzmm formats and organizes for you. You’ll need:</p>
<ul>
<li>The name of the model</li>
<li>A description of the model</li>
<li>The project name for SAS Model Manager</li>
<li>The modeler’s name or user ID</li>
<li>The model instance to be pickled</li>
<li>The data used to train the model</li>
<li>The expected input column names</li>
<li>The target variable name</li>
<li>The expected target output values</li>
<li>The name of the target output variables</li>
<li>Where the model files should be saved</li>
</ul>
<p>To keep it tidy and manageable, I like to put all of this into a single place:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">prefix <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'XGBoost'</span> <span style="color: #808080; font-style: italic;"># Model name</span>
model_desc <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;XGBoost model for hmeq&quot;</span> <span style="color: #808080; font-style: italic;"># Model description</span>
project <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;HMEQ Models&quot;</span> <span style="color: #808080; font-style: italic;"># Name of project</span>
modeler <span style="color: #66cc66;">=</span> <span style="color: #008000;">input</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Enter modeler username'</span><span style="color: black;">&#41;</span>
model   <span style="color: #66cc66;">=</span> xgb_model <span style="color: #808080; font-style: italic;"># Model instance</span>
data    <span style="color: #66cc66;">=</span> df_hmeq   <span style="color: #808080; font-style: italic;"># Data for model</span>
inputs  <span style="color: #66cc66;">=</span> X.<span style="color: black;">columns</span> <span style="color: #808080; font-style: italic;"># Input columns</span>
target  <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'BAD'</span>     <span style="color: #808080; font-style: italic;"># Target variable</span>
target_values <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;0&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;1&quot;</span><span style="color: black;">&#93;</span> <span style="color: #808080; font-style: italic;"># Target values: 0/1 for HMEQ</span>
target_cols   <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;EM_CLASSIFICATION&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">&quot;EM_EVENTPROBABILITY&quot;</span><span style="color: black;">&#93;</span> <span style="color: #808080; font-style: italic;"># Model output variables</span>
model_path    <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'/workspaces/myfolder/models'</span> <span style="color: #808080; font-style: italic;"># Path to model files</span></pre></td></tr></table></div>

<p>Once you have all of this, you can invoke the features of pzmm to automatically convert it into files that SAS Model Manager can use. Let’s take a look at each step.</p>
<h2>Step 1: Pickle it</h2>
<p>pzmm has a built-in method, <tt><a href="https://sassoftware.github.io/python-sasctl/api/pzmm/pickle_model.html#sasctl.pzmm.pickle_model.PickleModel.pickle_trained_model"><strong>pickle_trained_model</strong></a></tt>, which will pickle your model and put it in same folder you’ll be writing all the other files to.</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">pzmm.<span style="color: black;">PickleModel</span>.<span style="color: black;">pickle_trained_model</span><span style="color: black;">&#40;</span>
    model_prefix<span style="color: #66cc66;">=</span>prefix<span style="color: #66cc66;">,</span>
    trained_model<span style="color: #66cc66;">=</span>model<span style="color: #66cc66;">,</span>
    pickle_path<span style="color: #66cc66;">=</span>model_path
<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>That’s all there is to it!</p>
<h2>Step 2: Define inputs and outputs</h2>
<p>SAS Model Manager needs to know what the model inputs and outputs are to correctly score data. A single method does this: <tt><a href="https://sassoftware.github.io/python-sasctl/api/pzmm/write_json_files.html#sasctl.pzmm.write_json_files.JSONFiles.write_var_json"><strong>write_var_json</strong></a></tt>. As you might expect from the name, it saves all this information to JSON files.</p>
<p><strong>Model Inputs</strong></p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">pzmm.<span style="color: black;">JSONFiles</span>.<span style="color: black;">write_var_json</span><span style="color: black;">&#40;</span>
    input_data<span style="color: #66cc66;">=</span>data<span style="color: black;">&#91;</span>inputs<span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span>
    is_input<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: #66cc66;">,</span>
    json_path<span style="color: #66cc66;">=</span>model_path
<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p><strong>Model Outputs</strong></p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">output_var <span style="color: #66cc66;">=</span> pd.<span style="color: black;">DataFrame</span><span style="color: black;">&#40;</span>columns<span style="color: #66cc66;">=</span>target_cols<span style="color: #66cc66;">,</span> data<span style="color: #66cc66;">=</span><span style="color: black;">&#91;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;A&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">0.5</span><span style="color: black;">&#93;</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
pzmm.<span style="color: black;">JSONFiles</span>.<span style="color: black;">write_var_json</span><span style="color: black;">&#40;</span>
    output_var<span style="color: #66cc66;">,</span>
    is_input<span style="color: #66cc66;">=</span><span style="color: #008000;">False</span><span style="color: #66cc66;">,</span>
    json_path<span style="color: #66cc66;">=</span>model_path
<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>Note that the output data might seem a little strange at first. What we’re doing is creating a DataFrame with (1) the expected output variable names and (2) examples of the expected output types. We’re using “A” and “0.5” as examples to indicate that the first column (EM_CLASSIFICATION) will be labels while the second column (EM_EVENTPROBABILITY) will be probabilities. If you open up <tt><strong>outputVar.json</strong></tt>, you’ll see it automatically interpreted the example output values of “A” and “0.5” to be strings and intervals:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: black;">&#91;</span>
    <span style="color: black;">&#123;</span>
        <span style="color: #483d8b;">&quot;name&quot;</span>: <span style="color: #483d8b;">&quot;EM_CLASSIFICATION&quot;</span><span style="color: #66cc66;">,</span>
        <span style="color: #483d8b;">&quot;level&quot;</span>: <span style="color: #483d8b;">&quot;nominal&quot;</span><span style="color: #66cc66;">,</span>
        <span style="color: #483d8b;">&quot;type&quot;</span>: <span style="color: #483d8b;">&quot;string&quot;</span><span style="color: #66cc66;">,</span>
        <span style="color: #483d8b;">&quot;length&quot;</span>: <span style="color: #ff4500;">1</span>
    <span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span>
    <span style="color: black;">&#123;</span>
        <span style="color: #483d8b;">&quot;name&quot;</span>: <span style="color: #483d8b;">&quot;EM_EVENTPROBABILITY&quot;</span><span style="color: #66cc66;">,</span>
        <span style="color: #483d8b;">&quot;level&quot;</span>: <span style="color: #483d8b;">&quot;interval&quot;</span><span style="color: #66cc66;">,</span>
        <span style="color: #483d8b;">&quot;type&quot;</span>: <span style="color: #483d8b;">&quot;decimal&quot;</span><span style="color: #66cc66;">,</span>
        <span style="color: #483d8b;">&quot;length&quot;</span>: <span style="color: #ff4500;">8</span>
    <span style="color: black;">&#125;</span>
<span style="color: black;">&#93;</span></pre></td></tr></table></div>

<p>Are you wondering why we’re using EM_CLASSIFICATION and EM_EVENTPROBABILITY as the variable names? Seasoned SAS veterans already know, but I’ll leave that as a history lesson for you. Leave a comment if you know!</p>
<h2>Step 3: What file does what?</h2>
<p>You can upload a lot of different types of files into SAS Model Manager, and of the things in there, it needs to know exactly which ones will allow it to score your data. That’s where <tt><a href="https://sassoftware.github.io/python-sasctl/api/pzmm/write_json_files.html#sasctl.pzmm.write_json_files.JSONFiles.write_file_metadata_json"><strong>write_file_metadata_json</strong></a></tt> comes in:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">pzmm.<span style="color: black;">JSONFiles</span>.<span style="color: black;">write_file_metadata_json</span><span style="color: black;">&#40;</span>model_prefix<span style="color: #66cc66;">=</span>prefix<span style="color: #66cc66;">,</span> json_path<span style="color: #66cc66;">=</span>model_path<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>If you look at the output file, <strong>file_metadata.json</strong>, it gives a role to each file:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: black;">&#91;</span>
    <span style="color: black;">&#123;</span>
        <span style="color: #483d8b;">&quot;role&quot;</span>: <span style="color: #483d8b;">&quot;inputVariables&quot;</span><span style="color: #66cc66;">,</span>
        <span style="color: #483d8b;">&quot;name&quot;</span>: <span style="color: #483d8b;">&quot;inputVar.json&quot;</span>
    <span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span>
    <span style="color: black;">&#123;</span>
        <span style="color: #483d8b;">&quot;role&quot;</span>: <span style="color: #483d8b;">&quot;outputVariables&quot;</span><span style="color: #66cc66;">,</span>
        <span style="color: #483d8b;">&quot;name&quot;</span>: <span style="color: #483d8b;">&quot;outputVar.json&quot;</span>
    <span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span>
    <span style="color: black;">&#123;</span>
        <span style="color: #483d8b;">&quot;role&quot;</span>: <span style="color: #483d8b;">&quot;score&quot;</span><span style="color: #66cc66;">,</span>
        <span style="color: #483d8b;">&quot;name&quot;</span>: <span style="color: #483d8b;">&quot;score_XGBoost.py&quot;</span>
    <span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span>
    <span style="color: black;">&#123;</span>
        <span style="color: #483d8b;">&quot;role&quot;</span>: <span style="color: #483d8b;">&quot;scoreResource&quot;</span><span style="color: #66cc66;">,</span>
        <span style="color: #483d8b;">&quot;name&quot;</span>: <span style="color: #483d8b;">&quot;XGBoost.pickle&quot;</span>
    <span style="color: black;">&#125;</span>
<span style="color: black;">&#93;</span></pre></td></tr></table></div>

<p>This step is critical: skipping it means that the Model Manager won’t know what each file is supposed to be used for, and it will be unable to score your data.</p>
<h2>Step 4: Build model properties</h2>
<p>Finally, as the last step before registering, you need to write out the model properties. This gives SAS Model Manager information about the model’s name, the Python version, algorithm, and more. <tt><a href="https://sassoftware.github.io/python-sasctl/api/pzmm/write_json_files.html#sasctl.pzmm.write_json_files.JSONFiles.write_model_properties_json"><strong>write_model_properties_json</strong></a></tt> does this for you.</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">pzmm.<span style="color: black;">JSONFiles</span>.<span style="color: black;">write_model_properties_json</span><span style="color: black;">&#40;</span>
    model_name<span style="color: #66cc66;">=</span>prefix<span style="color: #66cc66;">,</span>
    target_variable<span style="color: #66cc66;">=</span>target<span style="color: #66cc66;">,</span>      <span style="color: #808080; font-style: italic;"># Target variable to make predictions about (BAD in this case)</span>
    target_values<span style="color: #66cc66;">=</span>target_values<span style="color: #66cc66;">,</span> <span style="color: #808080; font-style: italic;"># Possible values for the target variable (0 or 1 for binary classification of BAD)</span>
    json_path<span style="color: #66cc66;">=</span>model_path<span style="color: #66cc66;">,</span>        <span style="color: #808080; font-style: italic;"># Where are all the JSON files?</span>
    model_desc<span style="color: #66cc66;">=</span>model_desc<span style="color: #66cc66;">,</span>       <span style="color: #808080; font-style: italic;"># Describe the model</span>
    model_algorithm<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;Ensemble&quot;</span><span style="color: #66cc66;">,</span>  <span style="color: #808080; font-style: italic;"># What kind of algorithm is it?</span>
    modeler<span style="color: #66cc66;">=</span>modeler <span style="color: #808080; font-style: italic;"># Who made the model?</span>
<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>If you take a look at <strong>ModelProperties.json</strong>, you can view what SAS Model Manager will add to the model properties once you register it.</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: black;">&#123;</span>
    <span style="color: #483d8b;">&quot;name&quot;</span>: <span style="color: #483d8b;">&quot;XGBoost&quot;</span><span style="color: #66cc66;">,</span>
    <span style="color: #483d8b;">&quot;description&quot;</span>: <span style="color: #483d8b;">&quot;XGBoost model for hmeq&quot;</span><span style="color: #66cc66;">,</span>
    <span style="color: #483d8b;">&quot;scoreCodeType&quot;</span>: <span style="color: #483d8b;">&quot;python&quot;</span><span style="color: #66cc66;">,</span>
    <span style="color: #483d8b;">&quot;trainTable&quot;</span>: <span style="color: #483d8b;">&quot;&quot;</span><span style="color: #66cc66;">,</span>
    <span style="color: #483d8b;">&quot;trainCodeType&quot;</span>: <span style="color: #483d8b;">&quot;Python&quot;</span><span style="color: #66cc66;">,</span>
    <span style="color: #483d8b;">&quot;algorithm&quot;</span>: <span style="color: #483d8b;">&quot;Ensemble&quot;</span><span style="color: #66cc66;">,</span>
    <span style="color: #483d8b;">&quot;function&quot;</span>: <span style="color: #483d8b;">&quot;classification&quot;</span><span style="color: #66cc66;">,</span>
    <span style="color: #483d8b;">&quot;targetVariable&quot;</span>: <span style="color: #483d8b;">&quot;BAD&quot;</span><span style="color: #66cc66;">,</span>
    <span style="color: #483d8b;">&quot;targetEvent&quot;</span>: <span style="color: #483d8b;">&quot;1&quot;</span><span style="color: #66cc66;">,</span>
    <span style="color: #483d8b;">&quot;targetLevel&quot;</span>: <span style="color: #483d8b;">&quot;Binary&quot;</span><span style="color: #66cc66;">,</span>
    <span style="color: #483d8b;">&quot;eventProbVar&quot;</span>: <span style="color: #483d8b;">&quot;P_1&quot;</span><span style="color: #66cc66;">,</span>
    <span style="color: #483d8b;">&quot;modeler&quot;</span>: <span style="color: #483d8b;">&quot;Stu Sztukowski &quot;</span><span style="color: #66cc66;">,</span>
    <span style="color: #483d8b;">&quot;tool&quot;</span>: <span style="color: #483d8b;">&quot;Python 3&quot;</span><span style="color: #66cc66;">,</span>
    <span style="color: #483d8b;">&quot;toolVersion&quot;</span>: <span style="color: #483d8b;">&quot;3.11.13&quot;</span><span style="color: #66cc66;">,</span>
    <span style="color: #483d8b;">&quot;properties&quot;</span>: <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
<span style="color: black;">&#125;</span></pre></td></tr></table></div>

<h2>Step 5: Connect to SAS Viya</h2>
<p>Use the <tt><a href="https://sassoftware.github.io/python-sasctl/api/sasctl.session.html"><strong>Session</strong></a></tt> function of sasctl to start a session on your SAS Viya server. You have a few different authentication methods available:</p>
<ul>
<li>Username/password</li>
<li>Client ID and secret</li>
<li>OAuth token</li>
</ul>
<p>Your administrator can help you figure out the appropriate authentication method. Here is one example of how you might connect using a username and password:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">sess <span style="color: #66cc66;">=</span> Session<span style="color: black;">&#40;</span>
    <span style="color: #483d8b;">'https://my-viya-server.com'</span><span style="color: #66cc66;">,</span>
    username<span style="color: #66cc66;">=</span><span style="color: #008000;">input</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Enter username'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span>
    password<span style="color: #66cc66;">=</span><span style="color: #dc143c;">getpass</span>.<span style="color: #dc143c;">getpass</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Enter password'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span>
    protocol<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'https'</span><span style="color: #66cc66;">,</span>
    verify_ssl<span style="color: #66cc66;">=</span><span style="color: #008000;">False</span>
<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<h2>Step 6: Register your model</h2>
<p>You’re now ready to register your model to SAS Viya. Use <tt><a href="https://sassoftware.github.io/python-sasctl/api/pzmm/import_model.html#sasctl.pzmm.import_model.ImportModel.import_model"><strong>import_model</strong></a></tt> from pzmm to zip up all the files and register your model automatically. When you do this, lots is happening behind the scenes: a project is built, model files are loaded, and a plethora of checks are done to ensure that it all is going as planned.</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">pzmm.<span style="color: black;">ImportModel</span>.<span style="color: black;">import_model</span><span style="color: black;">&#40;</span>
    model_files    <span style="color: #66cc66;">=</span> model_path<span style="color: #66cc66;">,</span>    <span style="color: #808080; font-style: italic;"># Where are the model files?</span>
    model_prefix   <span style="color: #66cc66;">=</span> prefix<span style="color: #66cc66;">,</span>        <span style="color: #808080; font-style: italic;"># What is the model name?</span>
    project        <span style="color: #66cc66;">=</span> project<span style="color: #66cc66;">,</span>       <span style="color: #808080; font-style: italic;"># What is the project name?</span>
    input_data     <span style="color: #66cc66;">=</span> X<span style="color: #66cc66;">,</span>             <span style="color: #808080; font-style: italic;"># What does example input data look like?</span>
    predict_method <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span>xgb_model.<span style="color: black;">predict_proba</span><span style="color: #66cc66;">,</span> <span style="color: black;">&#91;</span><span style="color: #008000;">int</span><span style="color: #66cc66;">,</span> <span style="color: #008000;">int</span><span style="color: black;">&#93;</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> <span style="color: #808080; font-style: italic;"># What is the predict method and what does it return?</span>
    overwrite_model<span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span><span style="color: #66cc66;">,</span>          <span style="color: #808080; font-style: italic;"># Overwrite the model if it already exists?</span>
    score_metrics  <span style="color: #66cc66;">=</span> target_cols<span style="color: #66cc66;">,</span>   <span style="color: #808080; font-style: italic;"># What are the output variables?</span>
    target_values  <span style="color: #66cc66;">=</span> target_values<span style="color: #66cc66;">,</span> <span style="color: #808080; font-style: italic;"># What are the expected values of the target variable?</span>
    target_index   <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span>             <span style="color: #808080; font-style: italic;"># What is the index of the target value in target_values?</span>
    model_file_name<span style="color: #66cc66;">=</span> prefix + <span style="color: #483d8b;">&quot;.pickle&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #808080; font-style: italic;"># How was the model file serialized?</span>
    missing_values <span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span>           <span style="color: #808080; font-style: italic;"># Does the data include missing values?</span>
<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p>If successful, you will see the following message:</p>
<p><tt>The model was successfully imported into SAS Model Manager as XGBoost with the following UUID: {UUID}.</tt></p>
<p>Log in to SAS Model Manager and open the Projects page. Sort it in descending order by modified time and you’ll see your project at the top:</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/09/SAS-Model-Manager-Projects-Page.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/09/SAS-Model-Manager-Projects-Page.png" alt="" width="1915" height="979" class="aligncenter size-full wp-image-54086" srcset="https://blogs.sas.com/content/sgf/files/2025/09/SAS-Model-Manager-Projects-Page.png 1915w, https://blogs.sas.com/content/sgf/files/2025/09/SAS-Model-Manager-Projects-Page-300x153.png 300w, https://blogs.sas.com/content/sgf/files/2025/09/SAS-Model-Manager-Projects-Page-1024x523.png 1024w, https://blogs.sas.com/content/sgf/files/2025/09/SAS-Model-Manager-Projects-Page-768x393.png 768w, https://blogs.sas.com/content/sgf/files/2025/09/SAS-Model-Manager-Projects-Page-1536x785.png 1536w" sizes="(max-width: 1915px) 100vw, 1915px" /></a><br />
&nbsp;<br />
Open the project and you’ll see your model:<br />
<a href="https://blogs.sas.com/content/sgf/files/2025/09/xgb-1.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/09/xgb-1.png" alt="" width="1907" height="364" class="aligncenter size-full wp-image-54092" /></a><br />
&nbsp;</p>
<h2>But what if I can’t connect to SAS Viya?</h2>
<p>You still have an option available. <tt>import_model</tt> is also a wrapper for two methods that you can run yourself:</p>
<ul>
<li><tt><a href="https://sassoftware.github.io/python-sasctl/api/pzmm/write_score_code.html#sasctl.pzmm.write_score_code.ScoreCode.write_score_code">write_score_code</a></tt></li>
<li><tt><a href="https://sassoftware.github.io/python-sasctl/api/pzmm/zip_model.html#sasctl.pzmm.zip_model.ZipModel.zip_files">zip_files</a></tt></li>
</ul>
<p>Running both will generate your score code and zip up everything into a nice package that you can then download to your machine and manually import into Model Manager through the UI.</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="python" style="font-family:monospace;">pzmm.<span style="color: black;">ScoreCode</span>.<span style="color: black;">write_score_code</span><span style="color: black;">&#40;</span>
    model_files    <span style="color: #66cc66;">=</span> model_path<span style="color: #66cc66;">,</span> <span style="color: #808080; font-style: italic;"># Where are the model files?</span>
    score_code_path<span style="color: #66cc66;">=</span> model_path<span style="color: #66cc66;">,</span>
    model_prefix   <span style="color: #66cc66;">=</span> prefix<span style="color: #66cc66;">,</span> <span style="color: #808080; font-style: italic;"># What is the model name?</span>
    project        <span style="color: #66cc66;">=</span> project<span style="color: #66cc66;">,</span> <span style="color: #808080; font-style: italic;"># What is the project name?</span>
    input_data     <span style="color: #66cc66;">=</span> X<span style="color: #66cc66;">,</span> <span style="color: #808080; font-style: italic;"># What does example input data look like?</span>
    predict_method <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span>xgb_model.<span style="color: black;">predict_proba</span><span style="color: #66cc66;">,</span> <span style="color: black;">&#91;</span><span style="color: #008000;">int</span><span style="color: #66cc66;">,</span> <span style="color: #008000;">int</span><span style="color: black;">&#93;</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> <span style="color: #808080; font-style: italic;"># What is the predict method and what does it return?</span>
    overwrite_model<span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span><span style="color: #66cc66;">,</span> <span style="color: #808080; font-style: italic;"># Overwrite the model if it already exists?</span>
    score_metrics  <span style="color: #66cc66;">=</span> target_cols<span style="color: #66cc66;">,</span>   <span style="color: #808080; font-style: italic;"># What are the output variables?</span>
    target_values  <span style="color: #66cc66;">=</span> target_values<span style="color: #66cc66;">,</span> <span style="color: #808080; font-style: italic;"># What are the expected values of the target variable?</span>
    target_index   <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #808080; font-style: italic;"># What is the index of the target value in target_values?</span>
    model_file_name<span style="color: #66cc66;">=</span> prefix + <span style="color: #483d8b;">&quot;.pickle&quot;</span><span style="color: #66cc66;">,</span> <span style="color: #808080; font-style: italic;"># How was the model file serialized?</span>
    missing_values <span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span><span style="color: #66cc66;">,</span> <span style="color: #808080; font-style: italic;"># Does the data include missing values?</span>
    is_viya4       <span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span>
<span style="color: black;">&#41;</span>
&nbsp;
pzmm.<span style="color: black;">ZipModel</span>.<span style="color: black;">zip_files</span><span style="color: black;">&#40;</span>
    model_files  <span style="color: #66cc66;">=</span> model_path<span style="color: #66cc66;">,</span> <span style="color: #808080; font-style: italic;"># Where are the model files?</span>
    model_prefix <span style="color: #66cc66;">=</span> prefix<span style="color: #66cc66;">,</span>     <span style="color: #808080; font-style: italic;"># What is the model name?</span>
    is_viya4     <span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span>        <span style="color: #808080; font-style: italic;"># Set to False if Viya 3.5 or earlier</span>
<span style="color: black;">&#41;</span></pre></td></tr></table></div>

<p><a href="https://blogs.sas.com/content/sgf/files/2025/09/models.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/09/models.png" alt="" width="453" height="433" class="aligncenter size-full wp-image-54104" srcset="https://blogs.sas.com/content/sgf/files/2025/09/models.png 906w, https://blogs.sas.com/content/sgf/files/2025/09/models-300x287.png 300w, https://blogs.sas.com/content/sgf/files/2025/09/models-768x734.png 768w" sizes="(max-width: 453px) 100vw, 453px" /></a></p>
<p>Simply download the zip file to your machine, then log into SAS Model Manager and create a new modeling project. Open it up and select <em>Add Models</em>. From there, you can import your zip file.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/09/Add-models.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/09/Add-models.png" alt="" width="1151" height="251" class="aligncenter size-full wp-image-54107" srcset="https://blogs.sas.com/content/sgf/files/2025/09/Add-models.png 2302w, https://blogs.sas.com/content/sgf/files/2025/09/Add-models-300x65.png 300w, https://blogs.sas.com/content/sgf/files/2025/09/Add-models-1024x223.png 1024w, https://blogs.sas.com/content/sgf/files/2025/09/Add-models-768x167.png 768w, https://blogs.sas.com/content/sgf/files/2025/09/Add-models-1536x335.png 1536w, https://blogs.sas.com/content/sgf/files/2025/09/Add-models-2048x447.png 2048w" sizes="(max-width: 1151px) 100vw, 1151px" /></a></p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/09/XGBoost.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/09/XGBoost.png" alt="" width="741" height="431" class="aligncenter size-full wp-image-54110" srcset="https://blogs.sas.com/content/sgf/files/2025/09/XGBoost.png 1482w, https://blogs.sas.com/content/sgf/files/2025/09/XGBoost-300x174.png 300w, https://blogs.sas.com/content/sgf/files/2025/09/XGBoost-1024x596.png 1024w, https://blogs.sas.com/content/sgf/files/2025/09/XGBoost-768x447.png 768w" sizes="(max-width: 741px) 100vw, 741px" /></a></p>
<p>Now you know where the <strong>zip</strong> part comes from in Python <strong>Zip</strong> Model Management!</p>
<h2>Run a scoring test</h2>
<p>It’s a good idea to confirm that your model will run successfully and that SAS Viya isn’t missing any packages that the model needs. Running a scoring test on the model is a quick way to find out. You can run a new test from your project’s <em>Scoring</em> tab. Click the <em>New Test</em> button and fill out the form. Make sure that the data you upload is in the exact format that your model expects.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/09/XGBoost-Score-Test.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/09/XGBoost-Score-Test.png" alt="" width="562" height="520" class="aligncenter size-full wp-image-54113" srcset="https://blogs.sas.com/content/sgf/files/2025/09/XGBoost-Score-Test.png 1124w, https://blogs.sas.com/content/sgf/files/2025/09/XGBoost-Score-Test-300x278.png 300w, https://blogs.sas.com/content/sgf/files/2025/09/XGBoost-Score-Test-1024x947.png 1024w, https://blogs.sas.com/content/sgf/files/2025/09/XGBoost-Score-Test-768x711.png 768w" sizes="(max-width: 562px) 100vw, 562px" /></a></p>
<p>The test page will show you if it was successful. You can dive further into the results to confirm that it is working by looking at the log and output.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/09/Output-Table.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/09/Output-Table.png" alt="" width="699" height="446" class="aligncenter size-full wp-image-54116" srcset="https://blogs.sas.com/content/sgf/files/2025/09/Output-Table.png 1398w, https://blogs.sas.com/content/sgf/files/2025/09/Output-Table-300x191.png 300w, https://blogs.sas.com/content/sgf/files/2025/09/Output-Table-1024x653.png 1024w, https://blogs.sas.com/content/sgf/files/2025/09/Output-Table-768x490.png 768w" sizes="(max-width: 699px) 100vw, 699px" /></a></p>
<p>If it all looks good, then you’re done! Your model is now ready to be <a href="https://go.documentation.sas.com/doc/en/mdlmgrcdc/v_058/mdlmgrug/p04wolab4twpdwn1lipa21g8ozwo.htm">published to a multitude of destinations and put to work</a>.</p>
<h2>There’s no place like home</h2>
<p>One of the most amazing things about SAS Viya Workbench is how you can spin up powerful environments in seconds yet still have that feeling of running code directly from your laptop. If you’ve been building Python models and registering them into SAS Model Manager already, then you already know how to do it on SAS Viya Workbench. It really is that easy. But we’ve just covered the basics. There’s so much more you can do, like <a href="https://communities.sas.com/t5/SAS-Communities-Library/Creating-a-Model-Nutrition-Label-Model-Cards-for-Python-Models/ta-p/938661">build Model Cards</a>, <a href="https://sassoftware.github.io/python-sasctl/api/services/model_management.html">enable model performance monitoring</a>, and loads of other features you need for the last mile of AI.</p>
<p>What models do you find yourself working with most? How do you use Model Manager with your Python models? Let me know in the comments below!</p>
<h1>Links</h1>
<ul>
<li><a href="https://github.com/stu-code/register-xgboost-model/tree/main">Example Notebook: Registering Open Source Models to SAS Viya from SAS Viya Workbench</a></li>
<li><a href="https://github.com/sassoftware/model-management-resources">Model Management Resources for Open-Source Models</a></li>
<li><a href="https://medium.com/@cktaylor364/open-source-models-in-the-sas-viya-platform-fd87c880ccab">Open Source Models in the SAS Viya Platform</a></li>
<li><a href="https://medium.com/@rehabreda/registering-mlflow-models-to-sas-model-manager-using-sasctl-a-comprehensive-guide-a47dbf183338">Registering MLFlow Models to SAS Model Manager using sasctl: A Comprehensive Guide</a></li>
<li><a href="https://github.com/sassoftware/python-sasctl/tree/master/examples">sasctl and pzmm Examples</a></li>
<li><a href="https://sassoftware.github.io/python-sasctl/api/sasctl.html">sasctl and pzmm API Reference</a></li>
</ul>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2025/09/30/register-python-models-in-sas-viya-workbench/">Quickly and easily register Python models in SAS Viya Workbench</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></content:encoded>
					
		
		
			<enclosure url="https://blogs.sas.com/content/sgf/files/2025/09/SAS-Model-Manager-Projects-Page-150x150.png" />
	</item>
		<item>
		<title>Building seamless data pipelines across multiple languages: A life sciences and healthcare use case leveraging R and SAS</title>
		<link>https://blogs.sas.com/content/sgf/2025/08/07/building-seamless-data-pipelines-across-multiple-languages/</link>
					<comments>https://blogs.sas.com/content/sgf/2025/08/07/building-seamless-data-pipelines-across-multiple-languages/#comments</comments>
		
		<dc:creator><![CDATA[Emmett Smith]]></dc:creator>
		<pubDate>Thu, 07 Aug 2025 16:45:36 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[data pipelines]]></category>
		<category><![CDATA[healthcare analytics]]></category>
		<category><![CDATA[life sciences]]></category>
		<category><![CDATA[machine learning]]></category>
		<category><![CDATA[multi-language workflows]]></category>
		<category><![CDATA[R programming]]></category>
		<category><![CDATA[SAS]]></category>
		<category><![CDATA[SAS Viya Workbench]]></category>
		<category><![CDATA[Shiny app]]></category>
		<guid isPermaLink="false">https://blogs.sas.com/content/sgf/?p=53966</guid>

					<description><![CDATA[<p>Let SAS handle the data prep, R take care of the modeling, and skip the environment-hopping so your team can focus on building cool stuff faster.</p>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2025/08/07/building-seamless-data-pipelines-across-multiple-languages/">Building seamless data pipelines across multiple languages: A life sciences and healthcare use case leveraging R and SAS</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Managing multiple programming languages in a data science workflow often means jumping from one environment to another—adding friction to already complex processes. This slows down collaboration and innovation among teams. But what if there were a way to remove this friction between environments? Working in a single environment that supports multiple coding languages helps give teams time back for development, rather than managing tools. For example, being able to run Python, R, and SAS together in one environment eliminates the need to switch between two or three different platforms and allows experts to work in the language they’re most comfortable with.</p>
<p>In Life Sciences and Healthcare, working across different tools often means switching platforms or duplicating work. In this article, we explore how combining multiple programming languages within the same workflow can help reduce that friction—using SAS for data cleaning and exploration, and R for modeling and app development.</p>
<h2>Step 1: Clean and explore data</h2>
<p>The first thing you want to do is load your data. For this case, I’m using <a href="https://archive.ics.uci.edu/dataset/45/heart+disease">a dataset about classifying heart disease</a>. This dataset includes 14 variables selected by researchers to describe each patient and their current health. The original researchers had over 76 features and chose 13 to build linear regressions and feed into an algorithm based on Bayes Theorem. <a href="https://pubmed.ncbi.nlm.nih.gov/2756873/">The study is available online</a> if you're interested in reading more about it.</p>
<p>In a SAS Notebook, I first import the data before jumping into data cleaning by checking for missing values and replacing them with a relevant value. In this case, we see that only ca has missing values—this variable represents the number of major vessels visible during fluoroscopy. It ranges from 0 to 3, and if values are missing, we assume the test wasn’t performed on that patient, so we assign it a value of 0.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/08/The-MEANS-Procedure.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/08/The-MEANS-Procedure.png" alt="" width="2096" height="1200" class="aligncenter size-full wp-image-53975" srcset="https://blogs.sas.com/content/sgf/files/2025/08/The-MEANS-Procedure.png 2096w, https://blogs.sas.com/content/sgf/files/2025/08/The-MEANS-Procedure-300x172.png 300w, https://blogs.sas.com/content/sgf/files/2025/08/The-MEANS-Procedure-1024x586.png 1024w, https://blogs.sas.com/content/sgf/files/2025/08/The-MEANS-Procedure-768x440.png 768w, https://blogs.sas.com/content/sgf/files/2025/08/The-MEANS-Procedure-1536x879.png 1536w, https://blogs.sas.com/content/sgf/files/2025/08/The-MEANS-Procedure-2048x1173.png 2048w" sizes="(max-width: 2096px) 100vw, 2096px" /></a></p>
<p>Next, I checked to make sure that there is an even distribution of data between our two classes by plotting our target variable with a pie chart. Neither class has an overwhelming presence in the dataset, which means there is no need to balance the dataset.  This means that our quick data cleaning and exploration is done and that we can move over into our modeling in R.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/08/Heart-disease-percentage.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/08/Heart-disease-percentage.png" alt="" width="1816" height="982" class="aligncenter size-full wp-image-54012" srcset="https://blogs.sas.com/content/sgf/files/2025/08/Heart-disease-percentage.png 1816w, https://blogs.sas.com/content/sgf/files/2025/08/Heart-disease-percentage-300x162.png 300w, https://blogs.sas.com/content/sgf/files/2025/08/Heart-disease-percentage-1024x554.png 1024w, https://blogs.sas.com/content/sgf/files/2025/08/Heart-disease-percentage-768x415.png 768w, https://blogs.sas.com/content/sgf/files/2025/08/Heart-disease-percentage-1536x831.png 1536w" sizes="(max-width: 1816px) 100vw, 1816px" /></a></p>
<h2>Step 2: Transfer data into R and train gradient boosting model</h2>
<p>This step can be done in a couple of different ways. The first is a bit more roundabout—exporting the <tt>.sas7bdat</tt> files to a CSV and then reading that CSV into an R notebook. The second option is to download the <tt>haven</tt> library in R and use the <tt>read_sas()</tt> function to bring the data directly into R. I went with the second option before creating training and test datasets to train a gradient boosting model using the <tt>xgboost</tt> library.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/08/read-clean-data-in.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/08/read-clean-data-in.png" alt="" width="1588" height="1138" class="aligncenter size-full wp-image-54015" srcset="https://blogs.sas.com/content/sgf/files/2025/08/read-clean-data-in.png 1588w, https://blogs.sas.com/content/sgf/files/2025/08/read-clean-data-in-300x215.png 300w, https://blogs.sas.com/content/sgf/files/2025/08/read-clean-data-in-1024x734.png 1024w, https://blogs.sas.com/content/sgf/files/2025/08/read-clean-data-in-768x550.png 768w, https://blogs.sas.com/content/sgf/files/2025/08/read-clean-data-in-1536x1101.png 1536w, https://blogs.sas.com/content/sgf/files/2025/08/read-clean-data-in-269x192.png 269w" sizes="(max-width: 1588px) 100vw, 1588px" /></a></p>
<h2>Step 3: Display model performance with Shiny app</h2>
<p>We can now evaluate how well our model performed by gathering metrics like Accuracy, Recall, Precision, and F1 Score. Once these are calculated, we can create a simple Shiny app that allows users to choose which metric they want to view. This app can be as complex as you'd like—with interactive charts—or as simple as a drop-down menu.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/08/Accuracy-Precision-Recall.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/08/Accuracy-Precision-Recall.png" alt="" width="692" height="773" class="aligncenter size-full wp-image-53984" srcset="https://blogs.sas.com/content/sgf/files/2025/08/Accuracy-Precision-Recall.png 1384w, https://blogs.sas.com/content/sgf/files/2025/08/Accuracy-Precision-Recall-269x300.png 269w, https://blogs.sas.com/content/sgf/files/2025/08/Accuracy-Precision-Recall-917x1024.png 917w, https://blogs.sas.com/content/sgf/files/2025/08/Accuracy-Precision-Recall-768x858.png 768w, https://blogs.sas.com/content/sgf/files/2025/08/Accuracy-Precision-Recall-1375x1536.png 1375w" sizes="(max-width: 692px) 100vw, 692px" /></a></p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/08/Model-Evaluation-Metrics.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/08/Model-Evaluation-Metrics.png" alt="" width="3245" height="1118" class="aligncenter size-full wp-image-53987" srcset="https://blogs.sas.com/content/sgf/files/2025/08/Model-Evaluation-Metrics.png 3245w, https://blogs.sas.com/content/sgf/files/2025/08/Model-Evaluation-Metrics-300x103.png 300w, https://blogs.sas.com/content/sgf/files/2025/08/Model-Evaluation-Metrics-1024x353.png 1024w, https://blogs.sas.com/content/sgf/files/2025/08/Model-Evaluation-Metrics-768x265.png 768w, https://blogs.sas.com/content/sgf/files/2025/08/Model-Evaluation-Metrics-1536x529.png 1536w, https://blogs.sas.com/content/sgf/files/2025/08/Model-Evaluation-Metrics-2048x706.png 2048w" sizes="(max-width: 3245px) 100vw, 3245px" /></a></p>
<h2>Conclusion</h2>
<p>Bringing together the strengths of SAS and R creates a powerful, end-to-end data science workflow. SAS excels at data cleaning, transformation, and preparation—especially when working with large, structured datasets in enterprise environments. Once the data is clean and ready, R adds value as a powerful tool for building machine learning models using packages like xgboost. By integrating a Shiny app into the workflow, results become easily accessible and interactive. This seamless pipeline empowers teams to make data-driven decisions with clarity and confidence.</p>
<p>All this to say, there are three main takeaways from using a multi-language environment:</p>
<ol>
<li><strong>Choosing the best tool for the job</strong>: Each coding language can be used to excel in different areas of the pipeline.</li>
<li><strong>Collaborate across unique skill sets</strong>: Developers from different language backgrounds can bring their expertise to build dynamic teams. Each team can communicate seamlessly between components.</li>
<li><strong>Adapt to evolving tech trends</strong>: Each language brings new innovations, and by including multiple languages together, we can stay competitive and future-ready.</li>
</ol>
<p>Ultimately, a multi-language environment empowers developers to collaborate on scalable solutions faster and smarter.</p>
<h3>Learn more</h3>
<ul>
<li><a href="https://blogs.sas.com/content/sgf/2025/05/23/python-ml-pipelines-with-scikit-learn/">Python ML pipelines with Scikit-learn: A beginner’s guide</a></li>
<li><a href="https://blogs.sas.com/content/sgf/2025/08/01/ci-cd-for-python-and-sas/">CI/CD for Python and SAS: Build modern workflows with GitHub Actions</a></li>
<li><a href="https://blogs.sas.com/content/subconsciousmusings/2025/07/25/unlocking-the-power-of-sas-procedures/">Unlocking the power of SAS Procedures</a></li>
</ul>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2025/08/07/building-seamless-data-pipelines-across-multiple-languages/">Building seamless data pipelines across multiple languages: A life sciences and healthcare use case leveraging R and SAS</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blogs.sas.com/content/sgf/2025/08/07/building-seamless-data-pipelines-across-multiple-languages/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			<enclosure url="https://blogs.sas.com/content/sgf/files/2025/08/Model-Evaluation-Metrics-150x150.png" />
	</item>
		<item>
		<title>CI/CD for Python and SAS: Build modern workflows with GitHub Actions</title>
		<link>https://blogs.sas.com/content/sgf/2025/08/01/ci-cd-for-python-and-sas/</link>
		
		<dc:creator><![CDATA[Sean Ford]]></dc:creator>
		<pubDate>Fri, 01 Aug 2025 17:05:07 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[CI/CD]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[GitHub Actions]]></category>
		<category><![CDATA[Legacy Modernization]]></category>
		<category><![CDATA[Python Integration]]></category>
		<category><![CDATA[sas enterprise guide]]></category>
		<category><![CDATA[SAS Viya]]></category>
		<category><![CDATA[SAS Viya Workbench]]></category>
		<category><![CDATA[Workflow Automation]]></category>
		<guid isPermaLink="false">https://blogs.sas.com/content/sgf/?p=53813</guid>

					<description><![CDATA[<p>Learn how to modernize legacy SAS workflows by integrating Python and automating processes using GitHub Actions and SAS Viya Workbench, enabling seamless collaboration and CI/CD across development environments.</p>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2025/08/01/ci-cd-for-python-and-sas/">CI/CD for Python and SAS: Build modern workflows with GitHub Actions</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>You have years of legacy SAS code dating back to the time of your great-great-great-great grandparents (okay, SAS hasn’t been around quite <em>that</em> long). But, you want to automate your production jobs on your server and get your company into the modern era with version control and things like CI/CD. Oh, almost forgot, half your developers now want to include Python in your processes, right? So, you’ll need to be able to handle SAS, Python, and a mechanism for CI/CD and automation.</p>
<p>Your SAS developers are most likely scattered around the company. They like to use desktop applications like SAS Enterprise Guide, with a handful in the cloud on <a href="https://www.sas.com/en_us/software/viya/workbench.html">SAS Viya Workbench</a> or <a href="https://www.sas.com/en_us/software/analytics-pro.html">SAS Analytics Pro</a>. They might even say SAS can’t be modernized – but it most definitely can! And it will work well with your old SAS server just as easily as with the modern SAS analytics platforms like SAS Viya while integrating smoothly with Python.</p>
<p>The Python devs, well… Python can be the Wild West – VS Code, PyCharm, Spyder, Jupyter… the list goes on and on. When developing across languages, I personally like to use Viya Workbench because I have instant access to on-demand compute and can run both SAS and Python in the same dev environment, plus handle a lot of other things like shell scripts and yml.</p>
<p>Side note – yml or yaml, what gives? Are they the same? Do I say “yammel” or “why em el” when speaking to people so I don’t sound like a noob? Since we’re going to be working with GitHub ((i.e., Microsoft)) you’re going to see .yml extensions here. For all intents and purposes, yaml and yml are the same. It’s like the <a href="https://silicon-valley.fandom.com/wiki/Tabs_vs._spaces">tabs vs. spaces debate</a> in the show Silicon Valley – hilarious, yet entirely pointless. If you want to dive down the rabbit hole, see this fun Stack Overflow conversation: <a href="https://stackoverflow.com/questions/21059124/is-it-yaml-or-yml">Is it .yaml or .yml?</a></p>
<p>The example I’m laying out is not idle curiosity on my part, but one I’ve recently encountered with a company. I want to walk you through the steps we took to meet their needs, with a few additions and changes to help clarify how to modernize a legacy SAS workflow integrated with Python. The complete example code can be found at <a href="https://github.com/Shai-Alit/sas-workflow">this GitHub repo</a>.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/07/Basic-Legacy-Process.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/07/Basic-Legacy-Process.png" alt="Basic Legacy Process" width="1160" height="604" class="aligncenter size-full wp-image-53828" srcset="https://blogs.sas.com/content/sgf/files/2025/07/Basic-Legacy-Process.png 1160w, https://blogs.sas.com/content/sgf/files/2025/07/Basic-Legacy-Process-300x156.png 300w, https://blogs.sas.com/content/sgf/files/2025/07/Basic-Legacy-Process-1024x533.png 1024w, https://blogs.sas.com/content/sgf/files/2025/07/Basic-Legacy-Process-768x400.png 768w" sizes="(max-width: 1160px) 100vw, 1160px" /></a></p>
<p>The basic legacy process is shown above. The goal is to simplify this process by removing PuTTY and automating the script generation, while ensuring the production server automatically gets the most recent code. Because this is a legacy process using Enterprise Guide (EG) projects, we necessarily start there. However, the process outlined below will work just as well with code.</p>
<p>I’ll be using a slew of tools including EG, Viya Workbench, and GitHub to show you how you can collaborate across multiple SAS and Python development environments while taking advantage of automation features in GitHub via Actions and Workflows.</p>
<h2>Export the EG project to SAS code</h2>
<p>My example company here has been using projects within EG for many years and doesn’t want to lose their legacy work within those projects while moving forward using Python and SAS for advanced analytics; so we’re going to start our journey in EG. By the way, EG now <a href="https://communities.sas.com/t5/SAS-Communities-Library/Using-SAS-Enterprise-Guide-with-SAS-Viya-4/ta-p/921481">integrates with both Viya</a> and Viya Workbench. If you’re unfamiliar with EG, you can skip ahead since I’ll simply be exporting the project as raw SAS code and working from there.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/07/Process-Flow.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/07/Process-Flow.png" alt="" width="1510" height="680" class="aligncenter size-full wp-image-53831" srcset="https://blogs.sas.com/content/sgf/files/2025/07/Process-Flow.png 1510w, https://blogs.sas.com/content/sgf/files/2025/07/Process-Flow-300x135.png 300w, https://blogs.sas.com/content/sgf/files/2025/07/Process-Flow-1024x461.png 1024w, https://blogs.sas.com/content/sgf/files/2025/07/Process-Flow-768x346.png 768w" sizes="(max-width: 1510px) 100vw, 1510px" /></a></p>
<p>As you can see, I have a relatively simple project with a few process flows using both code and tasks. I’m using this project out of a GitHub repository that has a host of other things, including my automation scripts and code. To ensure my project plays nice with a Git repository, I have made sure to enable the relative paths for the project: Properties → File References → Use paths relative…</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/07/sas-workflow.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/07/sas-workflow.png" alt="" width="260" height="465" class="aligncenter size-full wp-image-53837" srcset="https://blogs.sas.com/content/sgf/files/2025/07/sas-workflow.png 520w, https://blogs.sas.com/content/sgf/files/2025/07/sas-workflow-168x300.png 168w" sizes="(max-width: 260px) 100vw, 260px" /></a></p>
<p>I’m also using the integrated Git features of EG to manage my project in GitHub from within the tool when working on my project.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/07/sas-workflow-EG.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/07/sas-workflow-EG.png" alt="" width="1130" height="546" class="aligncenter size-full wp-image-53840" srcset="https://blogs.sas.com/content/sgf/files/2025/07/sas-workflow-EG.png 1130w, https://blogs.sas.com/content/sgf/files/2025/07/sas-workflow-EG-300x145.png 300w, https://blogs.sas.com/content/sgf/files/2025/07/sas-workflow-EG-1024x495.png 1024w, https://blogs.sas.com/content/sgf/files/2025/07/sas-workflow-EG-768x371.png 768w" sizes="(max-width: 1130px) 100vw, 1130px" /></a></p>
<p>Since I am using legacy EG projects and don’t feel like pulling each piece of code out into its own file, I use the project level Share capability to Export All Code in Project.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/07/Export-All-Code-in-the-Project.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/07/Export-All-Code-in-the-Project.png" alt="" width="906" height="570" class="aligncenter size-full wp-image-53843" srcset="https://blogs.sas.com/content/sgf/files/2025/07/Export-All-Code-in-the-Project.png 906w, https://blogs.sas.com/content/sgf/files/2025/07/Export-All-Code-in-the-Project-300x189.png 300w, https://blogs.sas.com/content/sgf/files/2025/07/Export-All-Code-in-the-Project-768x483.png 768w, https://blogs.sas.com/content/sgf/files/2025/07/Export-All-Code-in-the-Project-343x215.png 343w" sizes="(max-width: 906px) 100vw, 906px" /></a></p>
<p>Now I can continue to work with my EG Project, but collaborate with others using Viya Workbench or Viya through GitHub, and keep most of my legacy production process in place.</p>
<p>Let’s move into Viya Workbench now, which has both SAS and Python runtime environments and an IDE that can handle any programming language, which will come in handy here where we’re dealing with SAS, Python, shell (bash), and yml.</p>
<h2>Integrate Python with the workflow</h2>
<p>The team wants to begin leveraging Python for this workflow. SAS code can easily integrate with Python through system commands and <a href="https://go.documentation.sas.com/doc/da/pgmsascdc/v_064/proc/p0sj9pq2ryjlphn1ceq7ntpc1ipp.htm">proc Python</a>, but in this case we’re going to take advantage of our GitHub action to run the necessary Python code and drop off data that our SAS code can pick up later. This approach has the advantage of letting the container running our action manage the Python package dependencies rather than having to manage those dependencies manually on our production server. Your IT team may either love you for this approach that doesn’t require them to maintain Python on the company server, or hate you because they are losing control.</p>
<p>Let’s add some Python code and test it out so we can see how our Python devs can join in the fun. To keep things simple, I’m going to create two files: one is a requirements file that will later inform our GitHub Action about the necessary Python dependencies, and the other contains the actual code.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/07/Python-folder.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/07/Python-folder.png" alt="" width="780" height="504" class="aligncenter size-full wp-image-53849" srcset="https://blogs.sas.com/content/sgf/files/2025/07/Python-folder.png 780w, https://blogs.sas.com/content/sgf/files/2025/07/Python-folder-300x194.png 300w, https://blogs.sas.com/content/sgf/files/2025/07/Python-folder-768x496.png 768w" sizes="(max-width: 780px) 100vw, 780px" /></a></p>
<p>And here are the contents of the files side-by-side. On the left is the requirements text file and the right is the Python script.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/07/requirements-generate_data.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/07/requirements-generate_data.png" alt="" width="1184" height="500" class="aligncenter size-full wp-image-53852" srcset="https://blogs.sas.com/content/sgf/files/2025/07/requirements-generate_data.png 1184w, https://blogs.sas.com/content/sgf/files/2025/07/requirements-generate_data-300x127.png 300w, https://blogs.sas.com/content/sgf/files/2025/07/requirements-generate_data-1024x432.png 1024w, https://blogs.sas.com/content/sgf/files/2025/07/requirements-generate_data-768x324.png 768w" sizes="(max-width: 1184px) 100vw, 1184px" /></a></p>
<p>Your requirements file should contain all package dependencies needed by your Python code (in this case, just pandas and numpy). The code in our example here is overly simple, while in reality you could have anything from complex ETL code to machine learning models. You could even have serialized models here (i.e., pickled models, ONNX, etc.) that run before handing information back over to the workflow in the form of a file or database update.</p>
<h2>Automate SAS scripts</h2>
<p>Remember, my goal here is to allow my SAS developers to work in any environment they are comforable in, collaborate with Python users, and automate at least part of my clunky old legacy process. So, instead of manually creating new shell scripts to run on my production Linux server every time someone updates the main branch, I want to automate that through GitHub Actions so neither my SAS nor Python developers have to ever worry about creating shell scripts.</p>
<p>I want to create a script that will look in my code folder and generate individual scripts for each SAS code file. Each script will run the SAS program in batch and write the log to a file that matches the program name plus the date and time. I want the date and time to match when the code is run (not when the script was created). To help me test this in my EG installation on my Windows desktop, I also create a batch file that does the same thing.</p>
<p>Most of the first 20 or so lines of the generate script really belong in a config file to improve portability across systems. But for simplicity, everything is currently hard-coded into the script.</p>
<p>The main loop looks through the target directory and builds a script and batch file for each SAS code file found in the target directory. These get dropped off in the <tt>/scripts</tt> directory. To summarize:</p>
<ul>
<li><tt>$TARGET_DIR</tt> – user defined folder to look for SAS code.</li>
<li><tt>-maxdepth 1</tt> – only look in the current folder. Don’t recurse.</li>
<li><tt>-type f</tt> – look for files (as opposed to directories).</li>
<li><tt>-name “*.sas”</tt> – only include files that end with the 4 characters “.sas”.</li>
</ul>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/08/generate_sh_scripts.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/08/generate_sh_scripts.png" alt="" width="1002" height="734" class="aligncenter size-full wp-image-53861" srcset="https://blogs.sas.com/content/sgf/files/2025/08/generate_sh_scripts.png 1002w, https://blogs.sas.com/content/sgf/files/2025/08/generate_sh_scripts-300x220.png 300w, https://blogs.sas.com/content/sgf/files/2025/08/generate_sh_scripts-768x563.png 768w" sizes="(max-width: 1002px) 100vw, 1002px" /></a></p>
<p>Once the script is finished, I can run it within Viya Workbench to test that it does what I want. One thing to confirm is that the script is executable. Line 34 does this for us on each script it creates so we don’t have to worry about it later, but if you have security concerns, you could omit this line and run the command prior to runtime on your production server. You’ll need to make sure to run the same command on the <tt>generate_sh_script.sh</tt> file before running.</p>
<p>From the Terminal (just click on the Terminal tab at the bottom of Viya Workbench) navigate to the scripts folder in your repo and run the script:</p>
<p><pre class="preserve-code-formatting" lang>
&gt;cd sas-workflow/scripts
&gt;chmod +x generate_sh_scripts.sh
&gt;./generate_sh_scripts.sh
</pre></p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/08/Terminal-Script.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/08/Terminal-Script.png" alt="" width="920" height="468" class="aligncenter size-full wp-image-53864" srcset="https://blogs.sas.com/content/sgf/files/2025/08/Terminal-Script.png 920w, https://blogs.sas.com/content/sgf/files/2025/08/Terminal-Script-300x153.png 300w, https://blogs.sas.com/content/sgf/files/2025/08/Terminal-Script-768x391.png 768w, https://blogs.sas.com/content/sgf/files/2025/08/Terminal-Script-164x82.png 164w" sizes="(max-width: 920px) 100vw, 920px" /></a></p>
<p>Note that you need to run this command from within the <tt>/scripts</tt> folder. We’ll make sure to take care of that with our GitHub action later on.</p>
<p>Voila, I now have my scripts. The single script I created will automatically generate <em>all</em> the necessary scripts needed for automation. That’s great, but I want to be able to run this script automatically, regardless of who adds code to the repository, so that my production process always has the most up-to-date code.</p>
<h2>Set up the GitHub Actions</h2>
<p><a href="https://docs.github.com/en/actions">GitHub Actions</a> is a powerful tool that lets you automate nearly anything you need. In this case, I’m going to use an action to build my shell scripts that will be picked up by my production server. It’s also going to run that Python code to create data that may be needed by the rest of my process.</p>
<p>When I initially got going on this project, I started with a preconfigured simple workflow and then added in the custom components I needed. This is typically the easiest way to get started with a new workflow. Try it out by going to your repository and click the Actions tab. From here, you can create simple workflows using preconfigured templates, custom workflows, or a wide array of other actions already created by other developers within the GitHub marketplace. If you’re feeling particularly creative, you can even add your own to the marketplace.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/08/Choose-a-workflow.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/08/Choose-a-workflow.png" alt="" width="1338" height="616" class="aligncenter size-full wp-image-53873" srcset="https://blogs.sas.com/content/sgf/files/2025/08/Choose-a-workflow.png 1338w, https://blogs.sas.com/content/sgf/files/2025/08/Choose-a-workflow-300x138.png 300w, https://blogs.sas.com/content/sgf/files/2025/08/Choose-a-workflow-1024x471.png 1024w, https://blogs.sas.com/content/sgf/files/2025/08/Choose-a-workflow-768x354.png 768w" sizes="(max-width: 1338px) 100vw, 1338px" /></a></p>
<p>Type “simple workflow” into the search and hit enter to find this easy initial workflow. Then commit it to your repo and GitHub will do the rest. You’ll notice it puts a .yml in a new folder called “.Github/workflows”. This is the default path in which GitHub looks for actions to perform. You don’t need to do anything else.</p>
<p>As I joked about earlier, the actual action is in a .yml (not .yaml) file. Once I create the basic template in GitHub for my workflow and push it to the repo, I can pull the repo into Viya Workbench and edit the file there. You could just as easily edit the yml file in GitHub, but I like the ease of Viya Workbench with its full Visual Studio Code IDE and the ability to use other extensions I installed on VS Code to do things like automatic linting so I don’t mess up the syntax.</p>
<p>Let’s peruse the action.yml file and note the things I added/modified from the simple workflow GitHub provides in the marketplace.</p>
<p>One of the most important components is the trigger, which tells GitHub under what conditions to actually run this action. The trigger I selected is any push to the main branch. There are a lot of <a href="https://docs.github.com/en/actions/how-tos/writing-workflows/choosing-when-your-workflow-runs/triggering-a-workflow">options</a> that let you get really specific with when you want to run the action. In our case, it might have been a good idea to use the paths option since we only want to recreate our scripts every time our SAS code is updated.</p>
<p><pre class="preserve-code-formatting" lang>
on:
   push:
      paths:
         - &#039;sub-project/**&#039;
         - &#039;!sub-project/docs/**
</pre></p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/08/automate_sas_action.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/08/automate_sas_action.png" alt="" width="1646" height="580" class="aligncenter size-full wp-image-53894" srcset="https://blogs.sas.com/content/sgf/files/2025/08/automate_sas_action.png 1646w, https://blogs.sas.com/content/sgf/files/2025/08/automate_sas_action-300x106.png 300w, https://blogs.sas.com/content/sgf/files/2025/08/automate_sas_action-1024x361.png 1024w, https://blogs.sas.com/content/sgf/files/2025/08/automate_sas_action-768x271.png 768w, https://blogs.sas.com/content/sgf/files/2025/08/automate_sas_action-1536x541.png 1536w" sizes="(max-width: 1646px) 100vw, 1646px" /></a></p>
<p>In the jobs section, note the runs-on section which specifies the OS to use. It also specifies the working directory. Remember above where we had to navigate to the scripts directory to run the generate script? This tells the container to nagivate to that same directory and work from there.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/08/Jobs-automate_sas_action.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/08/Jobs-automate_sas_action.png" alt="" width="1194" height="516" class="alignright size-full wp-image-53897" srcset="https://blogs.sas.com/content/sgf/files/2025/08/Jobs-automate_sas_action.png 1194w, https://blogs.sas.com/content/sgf/files/2025/08/Jobs-automate_sas_action-300x130.png 300w, https://blogs.sas.com/content/sgf/files/2025/08/Jobs-automate_sas_action-1024x443.png 1024w, https://blogs.sas.com/content/sgf/files/2025/08/Jobs-automate_sas_action-768x332.png 768w" sizes="(max-width: 1194px) 100vw, 1194px" /></a></p>
<p>And now for the fun stuff. Within the steps section, you need to use name/uses or name/run pairs. Here, you can write custom commands similar to those you'd use in the command line, or leverage actions available in the ever-growing GitHub Actions marketplace. I use both approaches here so you can see how they work.</p>
<p>To summarize the first handful of steps which set up our container:</p>
<ul>
<li>Line 26 – checkout the repo, using a predefined action from the marketplace.</li>
<li>Line 30 – set up the Python environment. Specify Python version 3.11.11.</li>
<li>Line 34 – install required Python dependencies listed in the requirements file within the repo.</li>
</ul>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/08/Install-automate_sas_action.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/08/Install-automate_sas_action.png" alt="" width="1152" height="730" class="aligncenter size-full wp-image-53900" srcset="https://blogs.sas.com/content/sgf/files/2025/08/Install-automate_sas_action.png 1152w, https://blogs.sas.com/content/sgf/files/2025/08/Install-automate_sas_action-300x190.png 300w, https://blogs.sas.com/content/sgf/files/2025/08/Install-automate_sas_action-1024x649.png 1024w, https://blogs.sas.com/content/sgf/files/2025/08/Install-automate_sas_action-768x487.png 768w" sizes="(max-width: 1152px) 100vw, 1152px" /></a></p>
<p>The next section of steps runs the desired commands and updates the repo:</p>
<ul>
<li>Line 38 – runs the generate script.</li>
<li>Line 42 – runs the Python data generation script.</li>
<li>Line 46/47 – this is important: tell the environment to use the default GITHUB_TOKEN. Without this, you won’t be able to push back to your repo.</li>
<li>Line 48 – Notice the pipe “|” which lets you run multiple lines.</li>
<li>Line 49/50 – this is required. For an auto action like this, I like to call the user some sort of bot and leave the email blank so it’s clear this was an automated action.</li>
<li>Line 51-53 – Git commands to add/commit/push.</li>
</ul>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/08/GitHub-commands-automate_sas_action.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/08/GitHub-commands-automate_sas_action.png" alt="" width="996" height="638" class="aligncenter size-full wp-image-53903" srcset="https://blogs.sas.com/content/sgf/files/2025/08/GitHub-commands-automate_sas_action.png 996w, https://blogs.sas.com/content/sgf/files/2025/08/GitHub-commands-automate_sas_action-300x192.png 300w, https://blogs.sas.com/content/sgf/files/2025/08/GitHub-commands-automate_sas_action-768x492.png 768w" sizes="(max-width: 996px) 100vw, 996px" /></a></p>
<p>And that’s it! This is a relatively straightforward action, but feel free to explore the <a href="https://docs.github.com/en/actions">documentation</a> for additional options.</p>
<p>One final gotcha: if you push this updated file as-is, the action will fail. That’s because it tries to push the new files back to the repository but doesn’t have the necessary permissions.</p>
<p>In a Viya Workbench terminal, type:</p>
<p><pre class="preserve-code-formatting" lang>
&gt;Git add –all
&gt;Git commit -m “added Github workflow”
&gt;Git push
</pre></p>
<p>Clicking on the attempt and looking at the failed action, we can see there’s a Permission Denied error on the add/commit/push step (line 45 in the actions.yml file).</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/08/added-github-workflow.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/08/added-github-workflow.png" alt="" width="964" height="296" class="aligncenter size-full wp-image-53906" srcset="https://blogs.sas.com/content/sgf/files/2025/08/added-github-workflow.png 964w, https://blogs.sas.com/content/sgf/files/2025/08/added-github-workflow-300x92.png 300w, https://blogs.sas.com/content/sgf/files/2025/08/added-github-workflow-768x236.png 768w" sizes="(max-width: 964px) 100vw, 964px" /></a></p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/08/build-automate-scripts.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/08/build-automate-scripts.png" alt="" width="1140" height="862" class="aligncenter size-full wp-image-53909" srcset="https://blogs.sas.com/content/sgf/files/2025/08/build-automate-scripts.png 1140w, https://blogs.sas.com/content/sgf/files/2025/08/build-automate-scripts-300x227.png 300w, https://blogs.sas.com/content/sgf/files/2025/08/build-automate-scripts-1024x774.png 1024w, https://blogs.sas.com/content/sgf/files/2025/08/build-automate-scripts-768x581.png 768w" sizes="(max-width: 1140px) 100vw, 1140px" /></a></p>
<p>To allow GitHub to push the changes made by the scripts back to the repo, you’ll need to make a change in your GitHub settings for your repo. Under the repo’s Settings tab, click on Actions → General and then scroll down to the Workflow Permissions section. Make sure you select the “Read and write permissions” radio button and save the changes.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/08/Workflow-permissions.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/08/Workflow-permissions.png" alt="" width="1400" height="500" class="aligncenter size-full wp-image-53912" srcset="https://blogs.sas.com/content/sgf/files/2025/08/Workflow-permissions.png 1400w, https://blogs.sas.com/content/sgf/files/2025/08/Workflow-permissions-300x107.png 300w, https://blogs.sas.com/content/sgf/files/2025/08/Workflow-permissions-1024x366.png 1024w, https://blogs.sas.com/content/sgf/files/2025/08/Workflow-permissions-768x274.png 768w" sizes="(max-width: 1400px) 100vw, 1400px" /></a></p>
<p>There’s a note that this uses the default GITHUB_TOKEN. Remember, we specified in the actions.yml file (line 47) that the environment should use this token. You could set up your own more granular permissions and/or use something like a personal access token if you wish, but I’ll stick to the default GitHub token here for simplicity’s sake.</p>
<p>Now, on my production server, all I need is one script that is scheduled using cron. I don’t cover that here, but it controls the entire process instead of having to manually create scheduled actions for each script. This one script pulls my repository from GitHub, which contains all my code and scripts, and then runs each script in the desired order. I never have to remember to check for new code or rebuild scripts.</p>
<h2>Summary</h2>
<p>We started with a legacy SAS EG project where the process to get the code to run on the production server was completely manual. In this walkthrough, we automated much of the process using Viya Workbench and GitHub Actions. We also integrated Python into the process, enabling SAS and Python developers to collaborate through a shared GitHub repository. This setup combines elements from both environments and ensures the production process stays up to date automatically. This highlights how a legacy process can be quickly and easily modernized with no change to the original code in a way that will still work with open-source and more modern SAS programming and AI/ML techniques in SAS Viya Workbench and SAS Viya.</p>
<h3>Learn more</h3>
<ul>
<li><a href="https://blogs.sas.com/content/sgf/2025/04/04/integrate-git-repositories-via-ssh/">Integrate Git repositories via SSH</a></li>
<li><a href="https://communities.sas.com/t5/SAS-Communities-Library/Setting-Up-SAS-Viya-with-GitLab-CI-CD-Pipelines-Step-One/ta-p/934745">Setting Up SAS Viya with GitLab CI/CD Pipelines: Step One</a></li>
<li><a href="https://blogs.sas.com/content/sgf/2025/03/07/install-vs-code-extensions-offline/">Install VS Code extensions offline: step-by-step guide</a></li>
<li><a href="https://blogs.sas.com/content/sgf/2025/06/12/sas-notebooks-in-vs-code/">SAS Notebooks in VS Code: A developer’s guide</a></li>
</ul>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2025/08/01/ci-cd-for-python-and-sas/">CI/CD for Python and SAS: Build modern workflows with GitHub Actions</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></content:encoded>
					
		
		
			<enclosure url="https://blogs.sas.com/content/sgf/files/2025/07/Basic-Legacy-Process-150x150.png" />
	</item>
		<item>
		<title>Boost ML accuracy with hyperparameter tuning (with a fun twist)</title>
		<link>https://blogs.sas.com/content/sgf/2025/06/13/boost-ml-accuracy-with-hyperparameter-tuning/</link>
					<comments>https://blogs.sas.com/content/sgf/2025/06/13/boost-ml-accuracy-with-hyperparameter-tuning/#comments</comments>
		
		<dc:creator><![CDATA[Stu Sztukowski]]></dc:creator>
		<pubDate>Fri, 13 Jun 2025 16:19:14 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[Cross-Validation]]></category>
		<category><![CDATA[Deep Learning]]></category>
		<category><![CDATA[Higgs boson]]></category>
		<category><![CDATA[Hyperparameter Tuning]]></category>
		<category><![CDATA[machine learning]]></category>
		<category><![CDATA[model interpretability]]></category>
		<category><![CDATA[model optimization]]></category>
		<category><![CDATA[neural networks]]></category>
		<category><![CDATA[SAS Viya Workbench]]></category>
		<guid isPermaLink="false">https://blogs.sas.com/content/sgf/?p=53723</guid>

					<description><![CDATA[<p>Hyperparameter autotuning intelligently optimizes machine learning model performance by automatically testing parameter combinations, balancing accuracy and generalizability, as demonstrated in a real-world particle physics use case.</p>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2025/06/13/boost-ml-accuracy-with-hyperparameter-tuning/">Boost ML accuracy with hyperparameter tuning (with a fun twist)</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Building a machine learning model isn’t always as easy as running .fit() and calling it a day. Sometimes, you need to eke out a little more accuracy, because even a 1% improvement can mean a lot to the bottom line. Many machine learning models have a lot of buttons and knobs you can adjust. Changing one value here, tweaking another value there, checking the accuracy one at a time, making sure it’s generalizable and not overfitting… it’s a lot of work to find the right model. Needless to say, trying all of these different combinations by hand can be a tedious task. But it doesn’t have to be. We can have the computer do it for us, and more importantly, intelligently. Enter: hyperparameter autotuning. Let’s talk about what it is first.</p>
<h1>Like baking a cake: a gentle introduction to hyperparameter autotuning</h1>
<p>A hyperparameter is a configuration value that you set before training a machine learning model. It controls how the learning happens. Think of it like baking a cake:</p>
<ul>
<li>The recipe is your model</li>
<li>The cake batter is your training data</li>
<li>The oven temperature, baking time, and pan size are your hyperparameters</li>
</ul>
<p>Let’s say your standard recipe says to use a 12-inch diameter cake pan and bake it at 350 degrees Fahrenheit for 30 minutes. These are the suggested values that make a good cake in most situations. But what if you wanted a smaller cake? Should you change the temperature? What about the baking time? If you have prior knowledge of what works, then you can make some estimates of what cooking time and even temperature to use. If you had an unlimited amount of cake batter, you can try as many combinations of these as you wanted to, learning from what works and what doesn’t to bake the <em>perfect</em> cake. That’s what intelligent hyperparameter autotuning does.</p>
<p>When you perform hyperparameter autotuning on your model, you’re asking the computer to try different combinations of hyperparameters and estimate how it improves a performance metric. For example: sum of squared errors, accuracy, misclassification rate, F1 score, etc. Your goal is to maximize or minimize some metric against validation data.</p>
<h2>Overfitting can ruin your cake</h2>
<p>So why don’t we just find the absolute <em>best </em>set of hyperparameters for every model all the time? There’s a catch to this: you’re at risk of overfitting your data, you’re making your model harder to interpret, it can take a lot of time, and it’s computationally expensive. When you overfit your training data, the model is harder to generalize. Even worse, when you start changing values of hyperparameters, your model is harder to understand. To see why this is, let’s go back to our cake analogy.</p>
<p>You’ve made a bunch of cake batter using your favorite ingredients and derived the best instructions for the ultimate cake. You spent hundreds of dollars on ingredients, countless hours experimenting, and have one hefty electric bill. But this is all a one-time cost, right? You invite your friends over to try your ultimate cake, and they agree: it’s an incredible cake. One of your friends says they’re having a party and wants you to bake a cake for it. Your friend has all the ingredients you need and even offered their oven and kitchen. On the day of the party, you go to your friend’s house and bake the cake using your precise instructions. Your friend says to everyone, “This is the <em>best </em>cake you will <em>ever </em>eat!” When they all taste it, it’s…just okay. Not bad, not great, but certainly not living up to the hype. What happened?</p>
<p>You think back to what could have gone wrong. You know you used the same instructions as you did last time, and you definitely used the same type of ingredients. But wait: your friend had slightly different <em>forms</em> of ingredients. The flour was organic, the sugar was cane sugar, and she had a gas convection oven while you had a standard electric. Your cake recipe was great for <em>your</em> <em>environment</em>, not <em>all</em> possible environments. Not only do all these things not work together with your instructions, but you don’t know <em>why</em> they don’t. You’re going to need to do even more experiments to generalize it better. Time to break out the credit card and start baking up a storm once again.</p>
<h2>Stress-testing the recipe</h2>
<p>When you’re performing hyperparameter autotuning, it’s vitally important to validate your results to help generalize the model. Real-world data is full of variance, and your training data may only capture some of it. One of the most common ways is k-fold cross-validation: split the data into <em>k</em> parts, train the model <em>k</em> times against <em>k-1</em> parts of data, reserving 1 for validation, then taking the average accuracy metric across all <em>k</em> trials. A well-generalized model performs accurately and consistently not only on the training data but also on new, unseen data, just like baking a cake in someone else’s kitchen with different variations of ingredients.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/06/Kitchens.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/06/Kitchens.png" alt="" width="463" height="436" class="alignright size-full wp-image-53738" srcset="https://blogs.sas.com/content/sgf/files/2025/06/Kitchens.png 926w, https://blogs.sas.com/content/sgf/files/2025/06/Kitchens-300x283.png 300w, https://blogs.sas.com/content/sgf/files/2025/06/Kitchens-768x723.png 768w" sizes="(max-width: 463px) 100vw, 463px" /></a></p>
<p>Imagine you have access to five kitchens, each stocked with all the ingredients and tools to bake a cake. Each kitchen is slightly different: one uses a gas oven while the other is electric, one has organic flour and the other has all-purpose flour, etc. You refine your recipe using four of the five kitchens, then try the final result in the fifth. You repeat this process four more times so that every kitchen is used at least once as the test kitchen.</p>
<p>The more consistent the results, the better your instructions generalize outside of ideal conditions. Cross-validation helps you avoid baking a cake that only works in your kitchen.</p>
<p>Validating your model under a variety of conditions is crucial to making sure it behaves in an expected way with real-world results. It’s even more important when you’ve tuned the hyperparameters. If it’s giving weird predictions, it will be hard to explain why it did what it did; and trust me, when an executive needs to know why your model missed the biggest and most important prediction of the year, “I don’t know” isn’t a great answer. You’re making a tradeoff between interpretability and performance when deciding whether to tune hyperparameters. If interpretability is important, use a simpler model. If accuracy is important, consider tuning the hyperparameters. If you need a bit of both, take a look at interpretability tools like <a href="https://blogs.sas.com/content/subconsciousmusings/2018/10/31/improving-model-interpretability-with-lime/">LIME</a> or <a href="https://communities.sas.com/t5/SAS-Communities-Library/How-to-Interpret-AI-and-Machine-Learning-Models-Using-Shapley/ta-p/915309">Shapley</a> to help you understand the results better.</p>
<p>All right, now that we understand what hyperparameter autotuning is, let’s get into an interesting use case in physics: colliding particles near the speed of light to find <a href="https://en.wikipedia.org/wiki/Higgs_boson">Higgs bosons</a>.</p>
<h1>Smashing particles like cakes: a real-world example</h1>
<p>In June of 2014, P. Baldi, P. Sadowski, and D. Whiteson of UC Irvine published the paper <a href="https://arxiv.org/pdf/1402.4735"><em>Searching for Exotic Particles in High-Energy Physics with Deep Learning</em></a>. Their goal was to use machine learning and deep learning to identify a signal vs. background particles after colliding two particles together: more specifically, Higgs bosons (the signal). According to their research, the Large Hadron Collider collides approximately 10<sup>11</sup> (100 billion) particles per hour. Of those particles, 300 of them are Higgs bosons – a 0.0000003% rate overall. That’s pretty rare.</p>
<p>To determine whether there is a signal or background particle, 28 measurements are used: 21 low-level features, and 7 high-level features. Low-level features are the basic kinematic properties recorded by particle detectors in the accelerator. In other words, these are raw values recorded by highly precise instruments. The 7 high-level features are derived by physicists to help discriminate between signals and background particles. These are manually derived, labor-intensive non-linear functions of the raw features. The overall goal of this research is to use deep learning to improve predictions of signals vs. background particles using low-level features. If proven true, this would eliminate the need to derive high-level features and allow a deep learning model to generate as good or better classifications as a result.</p>
<p>The researchers simulated 11M particle collisions with both low and high-level features. They used three types of models:</p>
<ol>
<li>Boosted Decision Tree</li>
<li>Shallow Neural Network</li>
<li>Deep Neural Network</li>
</ol>
<p>Within each model, they had three types of inputs:</p>
<ol>
<li>Low-level only</li>
<li>High-level only</li>
<li>Complete: both low-level and high-level</li>
</ol>
<p>They were able to successfully prove that a deep neural network of the low-level features outperformed the boosted decision tree and shallow neural network in all cases and even had equivalent performance when it included the complete feature set. Deep neural networks require a lot of computing power, time, fine-tuning, design, and in this case, GPU acceleration. They’re kind of like the multi-tier wedding cake of the modeling world: you need an expert who knows what they’re doing to build it, they have a ton of layers, and they take a lot of time and material to make.</p>
<h2>Can we bake a simpler cake?</h2>
<p>The paper tried out three models and ultimately landed on a deep neural network to achieve the highest level of prediction possible. What if we could use a simpler model with less computing power, like a smaller wedding cake whose taste still packs a punch?</p>
<p>Our goal: build a machine learning model with only low-level inputs that beats the boosted decision tree and shallow neural network for both low and high-level inputs. If we’re really lucky, we might even get close to the deep neural network. Let’s get started.</p>
<h2>The ingredients</h2>
<p><a href="https://archive.ics.uci.edu/dataset/280/higgs">The data</a> consists of 11M observations with 29 columns:</p>
<ul>
<li><strong>Signal</strong>: Binary indicator of a signal (1) or background (0)</li>
<li><strong>21 Low-level features</strong>: Raw measurements from a particle accelerator</li>
<li><strong>7 High</strong>-<strong>level features</strong>: Derived measurements from low-level features</li>
</ul>
<p>All columns are numeric, which is quite convenient for modeling.</p>
<h2>The kitchen</h2>
<p>The researchers used a 16-core, 64GB machine with an NVIDIA Tesla C2070 GPU. What I need is the ability to scale with minimal interruption, and SAS Viya Workbench does exactly that. The nice thing about SAS Viya Workbench is that I can choose multiple environment sizes. I can start small and scale up as needed, and it launches almost immediately after I make the change. For this case, we’ll go with a similar environment that the researchers had, especially since we’re working with a nearly 8GB csv file.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/06/SAS-Viya-Workbench-environment.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/06/SAS-Viya-Workbench-environment.png" alt="" width="566" height="342" class="aligncenter size-full wp-image-53741" srcset="https://blogs.sas.com/content/sgf/files/2025/06/SAS-Viya-Workbench-environment.png 1132w, https://blogs.sas.com/content/sgf/files/2025/06/SAS-Viya-Workbench-environment-300x181.png 300w, https://blogs.sas.com/content/sgf/files/2025/06/SAS-Viya-Workbench-environment-1024x619.png 1024w, https://blogs.sas.com/content/sgf/files/2025/06/SAS-Viya-Workbench-environment-768x464.png 768w" sizes="(max-width: 566px) 100vw, 566px" /></a></p>
<h2>Analyzing our ingredients</h2>
<p>Before embarking on any modeling mission, we need to see what we’re dealing with:</p>
<ul>
<li>Are there any missing values?</li>
<li>How balanced is the data?</li>
<li>Is multicollinearity going to be an issue?</li>
</ul>
<p>Using a standard data summary, such as described in polars or pandas, we can see there are no missing values of any variables. Perfect.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/06/Standard-data-summary.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/06/Standard-data-summary.png" alt="" width="1579" height="197" class="aligncenter size-full wp-image-53744" srcset="https://blogs.sas.com/content/sgf/files/2025/06/Standard-data-summary.png 2256w, https://blogs.sas.com/content/sgf/files/2025/06/Standard-data-summary-300x38.png 300w, https://blogs.sas.com/content/sgf/files/2025/06/Standard-data-summary-1024x128.png 1024w, https://blogs.sas.com/content/sgf/files/2025/06/Standard-data-summary-768x96.png 768w, https://blogs.sas.com/content/sgf/files/2025/06/Standard-data-summary-1536x192.png 1536w, https://blogs.sas.com/content/sgf/files/2025/06/Standard-data-summary-2048x256.png 2048w" sizes="(max-width: 1579px) 100vw, 1579px" /></a></p>
<p>It’s also nearly perfectly balanced with a 47/53 split of background (0)/signal (1).</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/06/Proportions-of-Signal.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/06/Proportions-of-Signal.png" alt="" width="666" height="484" class="aligncenter size-full wp-image-53747" srcset="https://blogs.sas.com/content/sgf/files/2025/06/Proportions-of-Signal.png 952w, https://blogs.sas.com/content/sgf/files/2025/06/Proportions-of-Signal-300x218.png 300w, https://blogs.sas.com/content/sgf/files/2025/06/Proportions-of-Signal-768x558.png 768w" sizes="(max-width: 666px) 100vw, 666px" /></a></p>
<p>And almost all variables have low correlation with each other.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/06/Correlation-of-Features.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/06/Correlation-of-Features.png" alt="" width="753" height="634" class="aligncenter size-full wp-image-53750" srcset="https://blogs.sas.com/content/sgf/files/2025/06/Correlation-of-Features.png 1076w, https://blogs.sas.com/content/sgf/files/2025/06/Correlation-of-Features-300x253.png 300w, https://blogs.sas.com/content/sgf/files/2025/06/Correlation-of-Features-1024x862.png 1024w, https://blogs.sas.com/content/sgf/files/2025/06/Correlation-of-Features-768x647.png 768w" sizes="(max-width: 753px) 100vw, 753px" /></a></p>
<p>The paper already describes the distribution of many of these variables, and in the interest of saving space, I’ll let you <a href="https://github.com/stu-code/higgs-prediction">take a look at the full Jupyter notebook</a> which graphs out many of these variables. All of them are fairly right-skewed which might mean we want to do some sort of transformation or scaling for our models, but it depends on which model we ultimately choose. But with all of these models out there, which one <em>should</em> we choose?</p>
<h2>Bakeoff: what’s the best vanilla cake?</h2>
<p>This data is fairly large, and if there’s one thing SAS truly excels at, it’s dealing with big datasets. This is especially true with multi-pass algorithms like gradient boosting or random forest. I did some initial tests with scikit-learn models, and the performance didn’t hold up; some models wouldn’t even finish. So, we’ll be turning to SAS models, which are also compatible with scikit-learn’s utilities to boot.</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="sas" style="font-family:monospace;"><span style="color: #0000ff;">from</span> sasviya.ml.linear_model import LogisticRegression
<span style="color: #0000ff;">from</span> sasviya.ml.svm import SVC
<span style="color: #0000ff;">from</span> sasviya.ml.tree import DecisionTreeClassifier, ForestClassifier, GradientBoostingClassifier</pre></td></tr></table></div>

<p>We’ll do a model bakeoff by running five SAS classification models through the gamut to see which comes out best. Here’s the setup:</p>
<ol>
<li>Create a set of pipelines that run default Logistic Regression, Support Vector Machine (SVM), Decision Tree, Random Forest, and Gradient Boosting</li>
<li>Transform with a Standard Scaler depending upon the model</li>
<li>Perform 5-fold cross-validation on all 11M observations</li>
<li>Compare the average accuracy and standard deviation</li>
</ol>
<p>This is like testing out variations of vanilla cakes before we turn it into something magnificent. The tastiest one wins. Here are the results:</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/06/Variation-testing.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/06/Variation-testing.png" alt="" width="410" height="238" class="aligncenter size-full wp-image-53753" srcset="https://blogs.sas.com/content/sgf/files/2025/06/Variation-testing.png 820w, https://blogs.sas.com/content/sgf/files/2025/06/Variation-testing-300x174.png 300w, https://blogs.sas.com/content/sgf/files/2025/06/Variation-testing-768x446.png 768w" sizes="(max-width: 410px) 100vw, 410px" /></a></p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/06/Default-Model-Accuracy-Comparison.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/06/Default-Model-Accuracy-Comparison.png" alt="" width="600" height="535" class="aligncenter size-full wp-image-53756" srcset="https://blogs.sas.com/content/sgf/files/2025/06/Default-Model-Accuracy-Comparison.png 1000w, https://blogs.sas.com/content/sgf/files/2025/06/Default-Model-Accuracy-Comparison-300x268.png 300w, https://blogs.sas.com/content/sgf/files/2025/06/Default-Model-Accuracy-Comparison-768x685.png 768w" sizes="(max-width: 600px) 100vw, 600px" /></a></p>
<p>Gradient boosting is the clear winner here, followed closely by random forest. This isn’t too surprising, as tree-based models tend to really shine. Even more impressive is that our standard deviation for accuracy is negligible, meaning there’s strong consistency across all our models.</p>
<p>We’re going to decide on gradient boosting. We don’t care too much about the interpretability of our model. We just want the best-performing, most generalizable model possible. You already know where this is going. Let’s do some hyperparameter autotuning.</p>
<h2>Fine-tuning the flavor</h2>
<p>We’re going to try tuning the hyperparameters of the gradient boosting model to see if we can get more performance. Not only that, we’re going to do it <em>intelligently</em>: we don’t want to randomly choose values and hope for the best. We want to try values that work and move in a direction that makes sense. This is where <a href="https://optuna.org/">Optuna</a> comes in.</p>
<p>Think of Optuna like a smart food critic who looks at every attempt and says “Hmm, that last cake was too dense. Maybe try lowering the oven temperature a bit next time.” It learns from each cake you bake, gradually steering you towards the perfect cake instead of blindly trying every temperature and time combination.</p>
<p>From a technical perspective, Optuna is an open source model-agnostic intelligent hyperparameter autotuning framework that uses Bayesian optimization to choose hyperparameters that work well. It builds a probabilistic model of which hyperparameters might work and tests the most promising ones next. This makes it faster, smarter, and more effective. In a world where time and compute are money, this is more important than ever.</p>
<p>Here's our plan:</p>
<ol>
<li>Split the data into 70% train, 15% validation, and 15% test</li>
<li>Autotune gradient boosting against the validation set for both low-level and high-level models</li>
<li>Verify the results with the untouched test dataset</li>
</ol>
<p>Because we have so much data and because we saw our models performed consistently in k-fold cross-validation, we’re going to tune our hyperparameters against a validation dataset, then confirm it with a test dataset. This is a great way to reduce computing time. Because we have a test dataset, we can make sure that our model is generalizable and not just tuned to the validation dataset.</p>
<h2>Optuna, our own personal Gordon Ramsay</h2>
<p>To do this, we’ll create a performance objective for Optuna to maximize or minimize, and tell it to run a study on that objective. In other words, we tell Optuna a range of hyperparameters to try, how many times to try a combination, and see what we end up with. It’s kind of like if we had Gordon Ramsay critiquing our cakes and suggesting different ways to improve them based on how it came out last time, just without all the shouting and deeply personal insults.</p>
<p>We’re going to optimize the ROC Area Under the Curve (AUC), which is the same metric that is used in the paper (note that this is different from <em>accuracy</em> that we used above for our initial model selection). If you’re unfamiliar with this metric, <a href="https://www.youtube.com/watch?v=aZCx_WB_dJ4">check out this comprehensive tutorial from Jeff Thompson about ROC charts in SAS and how to interpret them</a>. All you need to know right now is that it's a number ranging from 0 to 1. 0.5 means the model is as good as a coin flip, 1 means it predicts perfectly, and anything below 0.5 means it’s worse than random guessing. The closer to 1 you are, the better the model is at distinguishing between signals and background particles. According to the research paper, “small increases in AUC can represent significant enhancement in discovery significance”, so we’ll take any improvements we can get.</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="sas" style="font-family:monospace;">def low_level_objective<span style="color: #66cc66;">&#40;</span>trial<span style="color: #66cc66;">&#41;</span>:
    n_bins           = trial.suggest_int<span style="color: #66cc66;">&#40;</span><span style="color: #a020f0;">'n_bins'</span>, <span style="color: #2e8b57; font-weight: bold;">10</span>, <span style="color: #2e8b57; font-weight: bold;">100</span>, step=<span style="color: #2e8b57; font-weight: bold;">5</span><span style="color: #66cc66;">&#41;</span>
    n_estimators     = trial.suggest_int<span style="color: #66cc66;">&#40;</span><span style="color: #a020f0;">'n_estimators'</span>, <span style="color: #2e8b57; font-weight: bold;">50</span>, <span style="color: #2e8b57; font-weight: bold;">300</span>, step=<span style="color: #2e8b57; font-weight: bold;">10</span><span style="color: #66cc66;">&#41;</span>
    max_depth        = trial.suggest_int<span style="color: #66cc66;">&#40;</span><span style="color: #a020f0;">'max_depth'</span>, <span style="color: #2e8b57; font-weight: bold;">5</span>, <span style="color: #2e8b57; font-weight: bold;">17</span><span style="color: #66cc66;">&#41;</span>
    min_samples_leaf = trial.suggest_int<span style="color: #66cc66;">&#40;</span><span style="color: #a020f0;">'min_samples_leaf'</span>, <span style="color: #2e8b57; font-weight: bold;">100</span>, <span style="color: #2e8b57; font-weight: bold;">1000</span>, step=<span style="color: #2e8b57; font-weight: bold;">50</span><span style="color: #66cc66;">&#41;</span>
    learning_rate    = trial.suggest_float<span style="color: #66cc66;">&#40;</span><span style="color: #a020f0;">'learning_rate'</span>, <span style="color: #2e8b57; font-weight: bold;">0.01</span>, <span style="color: #2e8b57; font-weight: bold;">0.1</span>, step=<span style="color: #2e8b57; font-weight: bold;">0.01</span><span style="color: #66cc66;">&#41;</span>
&nbsp;
    model = GradientBoostingClassifier<span style="color: #66cc66;">&#40;</span>
        n_bins           = n_bins,
        n_estimators     = n_estimators,
        max_depth        = max_depth,
        min_samples_leaf = min_samples_leaf,
        learning_rate    = learning_rate,
        random_state     = <span style="color: #2e8b57; font-weight: bold;">42</span>
    <span style="color: #66cc66;">&#41;</span>
&nbsp;
    model.fit<span style="color: #66cc66;">&#40;</span>X_train_low, y_train<span style="color: #66cc66;">&#41;</span>
&nbsp;
    preds = model.predict_proba<span style="color: #66cc66;">&#40;</span>X_val_low<span style="color: #66cc66;">&#41;</span>.iloc<span style="color: #66cc66;">&#91;</span>:,<span style="color: #2e8b57; font-weight: bold;">1</span><span style="color: #66cc66;">&#93;</span>
    auc   = roc_auc_score<span style="color: #66cc66;">&#40;</span>y_val, preds<span style="color: #66cc66;">&#41;</span>
&nbsp;
    <span style="color: #0000ff;">return</span> auc</pre></td></tr></table></div>


<div class="wp_syntax"><table><tr><td class="code"><pre class="sas" style="font-family:monospace;">low_level_study = optuna.create_study<span style="color: #66cc66;">&#40;</span>
    direction  = <span style="color: #a020f0;">'maximize'</span>, 
    study_name = <span style="color: #a020f0;">'Low-level variables: Gradient Boosting Autotuning'</span>,
<span style="color: #66cc66;">&#41;</span>
&nbsp;
low_level_study.optimize<span style="color: #66cc66;">&#40;</span>low_level_objective, n_trials=<span style="color: #2e8b57; font-weight: bold;">30</span><span style="color: #66cc66;">&#41;</span></pre></td></tr></table></div>

<p>Now all we do is sit back, relax, and let Optuna do all the work.</p>
<h2>The final taste test</h2>
<p>Two SAS gradient boosting models were tuned: a model with low-level variables and a model with high-level variables. Each model went through 30 trials, meaning we ran <strong>60</strong> <strong>variations of gradient boosting models</strong>. The SAS models handled all these combinations without any concerns, even as complexity grew drastically. The fact that they handled these dozens of training iterations with ease shows just how well-optimized the platform is for use cases requiring serious computing power.</p>
<p>Optuna comes with some cool graphics. Let’s take a look at the tuning history to see how it learned across trials.</p>
<figure id="attachment_53759" aria-describedby="caption-attachment-53759" style="width: 1548px" class="wp-caption aligncenter"><a href="https://blogs.sas.com/content/sgf/files/2025/06/Low-level-gradient-boosting-model.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/06/Low-level-gradient-boosting-model.png" alt="Low-level gradient boosting model" width="1548" height="540" class="size-full wp-image-53759" srcset="https://blogs.sas.com/content/sgf/files/2025/06/Low-level-gradient-boosting-model.png 1548w, https://blogs.sas.com/content/sgf/files/2025/06/Low-level-gradient-boosting-model-300x105.png 300w, https://blogs.sas.com/content/sgf/files/2025/06/Low-level-gradient-boosting-model-1024x357.png 1024w, https://blogs.sas.com/content/sgf/files/2025/06/Low-level-gradient-boosting-model-768x268.png 768w, https://blogs.sas.com/content/sgf/files/2025/06/Low-level-gradient-boosting-model-1536x536.png 1536w" sizes="(max-width: 1548px) 100vw, 1548px" /></a><figcaption id="caption-attachment-53759" class="wp-caption-text">Low-level gradient boosting model</figcaption></figure>
<figure id="attachment_53762" aria-describedby="caption-attachment-53762" style="width: 1712px" class="wp-caption aligncenter"><a href="https://blogs.sas.com/content/sgf/files/2025/06/High-level-gradient-boosting-model.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/06/High-level-gradient-boosting-model.png" alt="High-level gradient boosting model" width="1712" height="576" class="size-full wp-image-53762" srcset="https://blogs.sas.com/content/sgf/files/2025/06/High-level-gradient-boosting-model.png 1712w, https://blogs.sas.com/content/sgf/files/2025/06/High-level-gradient-boosting-model-300x101.png 300w, https://blogs.sas.com/content/sgf/files/2025/06/High-level-gradient-boosting-model-1024x345.png 1024w, https://blogs.sas.com/content/sgf/files/2025/06/High-level-gradient-boosting-model-768x258.png 768w, https://blogs.sas.com/content/sgf/files/2025/06/High-level-gradient-boosting-model-1536x517.png 1536w" sizes="(max-width: 1712px) 100vw, 1712px" /></a><figcaption id="caption-attachment-53762" class="wp-caption-text">High-level gradient boosting model</figcaption></figure>
<p>You can very clearly see how Optuna tended to make both models better over time. This is the power of Bayesian hyperparameter autotuning: we don’t need to go through every possible combination. We give it some guard rails, and it will continue trying values that tend to work well. One interesting thing to note is how the high-level variables do such a good job predicting on their own. Remember, these are only 7 variables compared to the 21 raw inputs. It’s a true testament to the deep expertise of particle physicists who can mathematically derive useful variables that improve predictive power over the raw variables alone. This is an important example of how feature engineering using domain knowledge can be massively helpful for improving model performance.</p>
<p>Let’s compare <em>all</em> our models together with both our Validation and Test datasets to see how we did.</p>
<p><a href="https://blogs.sas.com/content/sgf/files/2025/06/Validation-vs-Test-ROC-Curves.png"><img loading="lazy" decoding="async" src="https://blogs.sas.com/content/sgf/files/2025/06/Validation-vs-Test-ROC-Curves.png" alt="" width="1168" height="626" class="aligncenter size-full wp-image-53765" srcset="https://blogs.sas.com/content/sgf/files/2025/06/Validation-vs-Test-ROC-Curves.png 1168w, https://blogs.sas.com/content/sgf/files/2025/06/Validation-vs-Test-ROC-Curves-300x161.png 300w, https://blogs.sas.com/content/sgf/files/2025/06/Validation-vs-Test-ROC-Curves-1024x549.png 1024w, https://blogs.sas.com/content/sgf/files/2025/06/Validation-vs-Test-ROC-Curves-768x412.png 768w" sizes="(max-width: 1168px) 100vw, 1168px" /></a></p>
<p>Thanks to hyperparameter autotuning, <strong>our champion model’s AUC improved by 15.2% over the </strong><strong> gradient boosting model, 8.9% over the boosted decision tree model, and 8.5% over the shallow neural network model. </strong>In addition, the results are almost identically consistent between the validation and test datasets. That’s excellent for generalization.</p>
<p>Here’s a breakdown of the tuned gradient boosting model compared to those in the paper:</p>
<table width="1000">
<tbody>
<tr>
<td width="274"><strong>Model</strong></td>
<td width="165"><strong>AUC: Low-level</strong></td>
<td width="165"><strong>Δ vs SAS (%)</strong></td>
<td width="165"><strong>AUC: High-level</strong></td>
<td width="165"><strong>Δ vs SAS (%)</strong></td>
</tr>
<tr>
<td width="274"><strong>SAS: Tuned Gradient Boosting</strong></td>
<td width="165"><strong>0.795 </strong></td>
<td width="165"><strong> </strong></td>
<td width="165"><strong>0.796</strong></td>
<td width="165"><strong> </strong></td>
</tr>
<tr>
<td width="274">Paper: Boosted Decision Tree</td>
<td width="165">0.73</td>
<td width="165"><strong>-8.2%</strong></td>
<td width="165">0.78</td>
<td width="165"><strong>-2.0%</strong></td>
</tr>
<tr>
<td width="274">Paper: Shallow Neural Network</td>
<td width="165">0.733</td>
<td width="165"><strong>-7.8%</strong></td>
<td width="165">0.777</td>
<td width="165"><strong>-2.4%</strong></td>
</tr>
<tr>
<td width="274">Paper: Deep Neural Network</td>
<td width="165">0.880</td>
<td width="165"><strong>+10.7%</strong></td>
<td width="165">0.885</td>
<td width="165"><strong>+11.2%</strong></td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p>The tuned gradient boosting model couldn't match the performance of a fully optimized deep neural network, but it outperformed the boosted decision tree, shallow neural network, and default SAS gradient boosting model — all on a modest CPU server, no GPU required.</p>
<h1>Blowing out the candles</h1>
<p>Hyperparameter autotuning is a powerful tool for squeezing out extra accuracy, but it comes with trade-offs: increased model complexity, higher risk of overfitting, and reduced interpretability. Not to mention, you’re going to be using more time and computing power. That’s why validation is critical. The more diverse and representative your data, the better your model will generalize. A model that performs well on the training set but falls apart in the real world may have simply learned to predict noise. It’s kind of like fondant: sure, it makes your cake look pretty, but it tastes downright awful.</p>
<p>In our case, we had a large, clean, balanced, and diverse dataset. Our ultimate goal was to improve accuracy, not interpretability, so with all the right conditions in place, autotuning helped get us the most out of our model. Sometimes you really can have your cake and eat it too.</p>
<h2>Links</h2>
<ul>
<li><a href="https://www.sas.com/en_us/webinars/sas-viya-workbench-demo.html">SAS® Viya® Workbench Demo: Solving Your Productivity Challenges</a></li>
<li><a href="https://github.com/stu-code/higgs-prediction">Check out the full detailed project on my GitHub</a></li>
<li><a href="https://archive.ics.uci.edu/dataset/280/higgs">UC Irvine Machine Learning Repository - Higgs boson dataset</a></li>
<li><a href="https://optuna.org/">Optuna - A model-agnostic hyperparameter optimization framework</a></li>
</ul>
<h2>References</h2>
<ul>
<li>Baldi, P., Sadowski, P., &amp; Whiteson, D. (2014, June 5). Searching for exotic particles in high-energy physics with Deep Learning. arXiv.org. <a href="https://arxiv.org/abs/1402.4735">https://arxiv.org/abs/1402.4735</a>.</li>
<li>Taylor, Lucas; CERN. <i>Simulated Higgs to two jets and two electrons.</i> Source: <a href="https://cds.cern.ch/record/628469" target="_blank" rel="noopener">cds.cern.ch</a>. Licensed under <a href="https://creativecommons.org/licenses/by-sa/4.0" target="_blank" rel="noopener">CC BY-SA 4.0</a>.</li>
<li>Whiteson, D. (2014). HIGGS [Dataset]. UCI Machine Learning Repository. <a href="https://doi.org/10.24432/C5V312">https://doi.org/10.24432/C5V312</a>. Licensed under <a href="https://creativecommons.org/licenses/by-sa/4.0">CC BY-SA 4.0</a>.</li>
</ul>
<p><a rel="nofollow" href="https://blogs.sas.com/content/sgf/2025/06/13/boost-ml-accuracy-with-hyperparameter-tuning/">Boost ML accuracy with hyperparameter tuning (with a fun twist)</a> was published on <a rel="nofollow" href="https://blogs.sas.com/content/sgf">SAS Users</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blogs.sas.com/content/sgf/2025/06/13/boost-ml-accuracy-with-hyperparameter-tuning/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			<enclosure url="https://blogs.sas.com/content/sgf/files/2025/06/CMS_Higgs-event-150x150.jpg" />
	</item>
	</channel>
</rss>
