Sisense Community logo
    • Community Feedback
    • Chapters
    • Events
    • Forums
      • Help and How To
      • Product Feedback Forum
      • Strategy & Use Cases
    • Blogs
    • KB Docs
      • KB Docs
      • Add-Ons & Plug-Ins
      • APIs
      • Best Practices
      • Blox
      • CDT
      • Cloud Managed Service
      • Data Models
      • Data Sources
      • Embedding Analytics
      • How-Tos & FAQs
      • Onboarding
      • PySisense
      • Security
      • Sisense Administration
      • Sisense Intelligence & AI
      • Troubleshooting
      • Widget & Dashboard Scripts
    • Support
    • Learning
      • Sisense Academy: Free Courses and Certifications
      • Official Developer Documentation
      • Official Product Documentation
      • Official Sisense Youtube Channel
      • Sisense Compose SDK Playground
      • Official Sisense Discord
    • Use Case Gallery
    Discussions
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
    Discussions
    • TagsChevronRightIcon
    CDT
      • CDTChevronRightIcon

      Every Kind Of JOIN

               

      More often than not, you'll need data from multiple different tables to create the perfect chart! There are many ways to do this but the most common way in SQL would be to use Joins!  There are four basic types of Joins: inner, left, right and full. Sisense also supports Cross Joins, Cross Database Joins, and our custom Automatic Join. Joins are placed after the FROM clause and is usually followed by an ON or USING clause where you specify the condition for joining. The syntax resembles the example below: select * from TableA left join TableB on TableA.id = TableB.user_id Basic Joins When describing the 4 basic Joins, it's very common to use venn diagrams to show what information is included in the resulting table. The colored sections show what would be included in the end. The following diagrams are from  sql-joins.com . 0. ON vs. USING The ON clause is used to tell the query which columns to join two tables on. For example, if there is a 'users' table with an 'id' column, and a 'purchases' table which has a 'user_id' column, we would need to specify that 'id' and 'user_id' is where the tables should be joined using a '='.  from users join purchases on user.id = purchases.user_id The USING clause is used when TableA and TableB share column names and we want to specify which shared column names to use for the match. If there is only one column in both tables that share the same name, neither the ON nor the USING clause is necessary. The USING clause is usually used when the tables share more than one column name and we need to specify which column names to match to. For example, let's say, 'users' and 'purchases' both have columns named 'user_id',  'first_name', and 'last_name' but we only want to match columns 'user_id', then we'd use the syntax below:  from users join purchases using (user_id) 1. INNER JOIN Inner Join returns all records from two tables only if they both meet the join condition. Inner Join is the default Join if the type is not specified. 2. LEFT JOIN Left Join returns all records from Table A and the matched records from Table B.  3. RIGHT JOIN Right Join returns all records from Table B and the matched records from Table A.  4. FULL JOIN Full Join will return all of the records if there is a match in either Table A or Table B.  5. OUTER JOIN Left, Right and Full Joins are all Outer Joins so you may see them written as "Left Outer Join." An outer join returns the rows that include what an Inner Join would return but also includes the rows that don't have a match in both tables. That's why you may see some blank cells when using an Outer Join.  Additional Joins 1. CROSS JOIN Cross Join returns a result that matches each item in Table A to every item in Table B. The size of the result set will be the number of rows in Table A multiplied by the number of rows in Table B. A Cross Join can actually be written without a JOIN clause and just separated by commas in the FROM clause. select * from TableA, TableB It can also be written by joining on 1=1. select * from TableA join TableB on 1=1 2. Cross-Database JOIN Cross-Database Joins  allow users to join tables from different databases by having the tables exist on the Sisense Cache or Sisense Managed Warehouse. The syntax is similar to a single database join with the addition of the database name preceding the table name.  select * from database1.user as TableA right join database2.purchases as TableB on TableA.id = TableB.user_id Cross-Database Joins must align with Redshift syntax rules and all the tables used in the join must be cached.  3. Automatic JOIN Sisense for Cloud Data Teams has created a shortcut to automatically Inner Join or Left Join tables together with a special syntax. These shortcuts will join the tables based on their primary keys, foreign keys, and common naming conventions. There cannot be whitespace within the brackets.  For Inner Joins: select * from [TableA+TableB] For Left Joins: select * from [TableA<TableB] 4. JOIN Views & CSVs With the Cache and Warehouse infrastructure, users can Join to a  CSV upload  or a  View . When joining CSVs and Views, all of the tables in the query must exist on the cache. The syntax is the same as joining two tables with the addition of brackets necessary for CSVs and Views. The alias of the CSV or View must be assigned within the brackets.  select * from TableA inner join [csv_or_view_name as TableB] on TableA.id = TableB.email Extra Documentation Here are the official Sisense for Cloud Data Team documents for  Cross-Database Joins ,  Automatic Joins ,  Joining CSVs , and  Joining Views . 

      intapiuser
      intapiuserPosted 3 years ago • Last reply 8 months ago
      1
               
    • Meghan Rote
      • Help and How-To
               
      Meghan Rote
      Migrate from CDT to Fusion
                       

      Has anyone migrated from CDT to Fusion? What was the experience like? Any tips/tricks/advice?

      1 year agolast reply 10 months ago
      6
               
      • CDTChevronRightIcon

      Your Guide To Default Filters

               

      Filters are a great way to slice and dice your data into a variety of views. There are a couple ways you can set default filter values, on the dashboard level and on the query level.   Dashboard Level Default Filters If you want all your users to view the same filter set for a dashboard upon login, select the desired filter configuration on your dashboard and click on "Set as Default" in the filter bar. Further details and screenshots can be found on our documentation  here . Note that email reports use the default filter configuration. If your users select a different set of filters and want to go back to the default that you set, they can hit "Reset Filters" to restore the default filter set up.   Chart Level Default Filters You can define the default filtering behavior in your query by using  Direct Replacement Filters  with pipe notation. This notation looks like this: [filter_name|default_value] Note that the default pipe notation cannot be used in filter standard notation. Ex [field_name=filter_name|default_value] will not return a result. Users can get creative with the default filter notation to define how exactly they want their charts to behave depending on filter selections. Refer to the following community posts for a few examples! Prompt Users to Enter a Filter Value if nothing is selected Filter out Selected Filter Values Filters in Case When Statements (Controlling Chart Behavior between Different Filters) How else have you creatively used default filters? Comment below!

      intapiuser
      intapiuserPosted 3 years ago • Last reply 1 year ago
      2
               
    • Blog banner
      • Best PracticesChevronRightIcon

      Leveraging Sisense Notebooks with Compose SDK

                                                                       

      Leveraging Sisense Notebooks with Compose SDK In today’s developer ecosystem, delivering a tailored creator experience is essential for success, especially when integrating analytics into customer-facing applications. Many existing solutions offer a generic, one-size-fits-all approach, typically centered around drag-and-drop interfaces. While these tools may be convenient, they often limit the depth of customization needed for unique business use cases. Additionally, platforms that offer more advanced capabilities usually require adopting proprietary syntax, which can drive up costs by necessitating specialized expertise. Sisense, however, provides a powerful code-first analytics solution that eliminates these barriers. It allows your data teams to leverage their existing skills—such as SQL, Python, and R—to create highly customized insights that align with your organization's specific needs, without needing proprietary languages or systems. In this article, we’ll explore how you can use SQL specifically to create custom datasets and charts, and then embed those visualizations into your web applications. This workflow emphasizes the creator experience for data teams, empowering them to build meaningful, business-specific analytics that enhances your product’s value. Building Custom Datasets with SQL Let’s dive into a specific use case. Assume you have a commerce table with two key columns: `transaction_amount` and `transaction_status`. You need to analyze revenue, categorizing it by transaction type (e.g., paid, refunded, or canceled). This provides insight into gross revenue versus potential revenue lost to refunds or cancellations. Below is the SQL query used to create a custom dataset for this scenario: ( Dataset Git Repo ) ( Data Model Repo  )     SELECT TO_CHAR(DATE_TRUNC('month', transaction_date), 'YYYY-MM') AS "Date", transaction_id, SUM(CASE WHEN transaction_status = 'Paid' THEN transaction_amount ELSE 0 END) AS "Actual Revenue", SUM(CASE WHEN transaction_status IN ('Refunded', 'Cancelled') THEN transaction_amount ELSE 0 END) AS "Potential Revenue" FROM commerce GROUP BY DATE_TRUNC('month', transaction_date), transaction_id;     This query creates a monthly summary of actual revenue and potential revenue lost due to refunds or cancellations. After running the query, you can preview the output in Sisense and immediately take action. Integrating Custom Data into Fusion Once your custom table is generated, you have several options for incorporating it into your analytics workflow: Adding Notebooks Charts to Dashboards   1. Integrate within the Semantic Model: The SQL table can be added to Sisense’s semantic layer, allowing you to build charts and dashboards on top of it using Fusion Dashboards or Compose SDK. 2. Build Charts for Integration : You can also directly build charts based on this table and pass them to your frontend team for integration into customer-facing applications. Streamlining Developer Workflows with Compose SDK Once your data team has completed their SQL-based analysis and crafted custom datasets and charts, the true value lies in how effortlessly developers can integrate these insights into customer-facing applications. By publishing the dataset to the Fusion semantic model , developers can use Sisense’s command-line tools (like '@sisense/sdk-cli') to dynamically generate TypeScript representations of the dataset right within their IDE. This streamlined process ensures that the work done by analysts is directly accessible to developers, enhancing the builder experience by providing immediate access to the datasets. This integration allows developers to efficiently embed highly customized, meaningful analytics into the front end, ensuring that the insights are not only visually compelling but also tailored to the specific needs of the end users. Creating Custom Visualizations Once charts have been built in Sisense Notebooks and published to Fusion , developers can begin integrating them seamlessly into front-end web applications using the Compose SDK . This process allows developers to embed dynamic, SQL-powered visualizations directly into customer-facing apps. By leveraging the Compose SDK, developers can access the published charts, configure them for specific frontend needs, and ensure a consistent, interactive user experience. This workflow streamlines the integration of custom analytics into web apps, providing flexibility and efficiency for development teams of all varieties. This code snippet demonstrates how to integrate both a custom column chart and an existing dashboard widget into a React application using Sisense’s Compose SDK. The `ColumnChart` component displays actual and potential revenue by year, while the `DashboardWidget` component integrates a pre-existing Sisense dashboard into the application. DashboardWidget   Sisense CLI       import { ColumnChart, DashboardById, DashboardWidget } from '@sisense/sdk-ui'; import * as DM from "../notebooks-tutorial" import { measureFactory } from '@sisense/sdk-data'; function ComposeSDK() { return ( <> <ColumnChart dataSet={DM.DataSource} dataOptions={{ category: [DM.ActualVsPotential.transaction_date.Years], value: [ measureFactory.sum(DM.ActualVsPotential.actual_revenue, 'Actual Revenue'), measureFactory.sum(DM.ActualVsPotential.potential_revenue, 'Potential Revenue'), ], breakBy: [DM.commerce.age_range], }} styleOptions={{ height: 500, }} /> <DashboardWidget dashboardOid="66e8aa5e724a0b00338cbed9" widgetOid="66e8c716724a0b00338cbefc" /> </> ); } export default ComposeSDK;   Conclusion:  This code-first approach to analytics meets technical creators where they are, empowering data teams and developers alike. Whether your analysts are leveraging their existing skills in SQL through the powerful Notebooks environment or your developers are dynamically generating insights within their IDE using the Compose SDK , Sisense offers a seamless and collaborative experience. By enabling teams to work with familiar tools and environments, Sisense allows you to deliver powerful, tailored analytics that fit your organization’s unique needs. This fluid, creator-friendly workflow ensures that both data teams and developers can focus on building impactful insights without the friction of proprietary systems, enhancing the overall efficiency and creativity of your analytics solutions.

      Sisense User
      Sisense UserPosted 1 year ago
      0
               
      • CDTChevronRightIcon

      Display df.describe() Output In A Dataframe

               

      The  .describe() function  in Python is really handy for exploratory analysis! Sometimes, you would want to display this as a table on a dashboard, but how? The output is typically printed out as text, not as a dataframe. Fear not, getting this into a dataframe is fairly painless! Before we go on, note that using Python requires the  Python/R Integration . Let's say this is the first 10 rows of your SQL output / starting dataframe (the data below is the User IDs and number of gameplays they made for a fictional gaming company) print(df['gameplay_number'].describe()) gives you the following output To get this in a table of its own (rather than a printout), use the code below! Note, the key in the dictionary used as an argument for pd.DataFrame is what we want to name the column of summary values in our final output. I chose to name this 'gameplays.' df2 = pd.DataFrame({'gameplays': df['gameplay_number'].describe()}) df2 = df2.reset_index() # Use Sisense for Cloud Data Teams to visualize a dataframe by passing the data to periscope.output() periscope.output(df2) And there you have it!

      intapiuser
      intapiuserPosted 3 years ago • Last reply 2 years ago
      1
               
      • CDTChevronRightIcon

      Radial Funnel Chart

               

      Here is an alternate visualization of a funnel chart - instead of using bars, it depicts the different steps as concentric circles. How to use: define an array in the extract() function of the values you'd like to use. If you are pulling data from a data frame, you will need to use something like the pandas iloc function to extract the desired values. import pandas as pd import matplotlib.pyplot as plt import numpy as np import matplotlib.patches as mpatches from matplotlib.collections import PatchCollection # extract important variables from the data frame def extract(): # GET DATA FROM DATA FRAME # if df.size == 0: # return [0, 0, 0 , 0, 0] # else: # opps_created = df.iloc[0]['opps_created'] # opps_accepted = df.iloc[0]['opps_accepted'] # opps_trialed = df.iloc[0]['opps_trialed'] # opps_won = df.iloc[0]['opps_won'] # EXAMPLE DATA opps_created = 150 opps_accepted = 80 opps_trialed = 50 opps_won = 20 return [opps_created, opps_accepted, opps_trialed, opps_won] #################### # create text chart #################### def kpi_text(ax, text = '', text_color = 'black', x_position = 0, y_position = 0.5, font_size = 13): return ax.text(x = x_position, y = y_position, s = text, color = text_color, family = 'sans-serif', fontsize = font_size, horizontalalignment = 'center', verticalalignment = 'center') # create the plot def kpi_chart(df): # set up the figure fig, ax = plt.subplots(figsize = (5.5,5.5)) fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0) ax.axis('off') # styling (colors and font sizes) mediumgray = '#999292' lightgray = '#c4c4c4' purples = plt.get_cmap('Purples_r') fontsize_text = 30 offset_x = 0.1 offset_y = 0.13 # parse the data funnel_df = extract() # use the following if you are using the extract_df function to parse the desired values # funnel_df = extract(df) [opps_created, opps_accepted, opps_trialed, opps_won] = funnel_df # map the data def get_data_domain(data): return {'min': min(data), 'max': max(data)} def data_mapper(data = [None], outputsize = 0.5): #assumes zero-max domain normalization # 0.48 is the max radius with a bit of padding domain = get_data_domain(data) mapped_area = [1.0 * d/domain['max'] * outputsize for d in data] mapped_radius = [np.sqrt(d/np.pi) for d in mapped_area] return mapped_radius # mapping the data mapped_df = data_mapper(funnel_df) colors = ['aliceblue', 'paleturquoise', 'darkturquoise', 'lightseagreen'] # the circles for i in range(len(mapped_df)): ax.add_patch( mpatches.Circle( (0.5 + offset_x, mapped_df[i] + offset_y), mapped_df[i], color = colors[i])) kpi_text(ax, text = funnel_df[-1], text_color = 'white', font_size = fontsize_text, x_position = 0.5 + offset_x, y_position = mapped_df[-1] + 0.01 + offset_y) for i in range(len(mapped_df) - 2, -1, -1): kpi_text(ax, text = funnel_df[i], text_color = 'teal', font_size = fontsize_text, x_position = 0.5 + offset_x, y_position = mapped_df[i + 1] + mapped_df[i] + 0.01 + + offset_y) cols = ['Created', 'Accepted', 'Trialed', 'Won'] for i in range(len(cols) - 2, -1, -1): kpi_text(ax, text = cols[i], text_color = 'steelblue', font_size = fontsize_text/1.7, x_position = 0.5 + offset_x, y_position = mapped_df[i] + mapped_df[i+1] - 0.05 + 0.01 + offset_y) kpi_text(ax, text = cols[-1], text_color = 'white', font_size = fontsize_text/1.7, x_position = 0.5 + offset_x, y_position = mapped_df[-1] - 0.05 + 0.01 + offset_y) return plt periscope.image(kpi_chart())

      intapiuser
      intapiuserPosted 3 years ago • Last reply 2 years ago
      2
               
      • CDTChevronRightIcon

      Creating A "Select All" Condition For A Multi-Select Filter!

               

      Often, users will have a large number of filter values selected in a multi-select filter, and rather than having to deselect or 'x' out every filter value, you may want to quickly check the full, unfiltered data.  This is something you can do in Sisense for Cloud Data Teams using a couple SQL ticks. We'll walk through how to do this using an NBA dashboard I built out for fun (using a CSV from early in the season - keep in mind it's not updated) Let's go through what it looks like! Here's the dashboard, completely unfiltered (note the values which appear on the charts so you can compare to below screenshots). And this is how it looks after I've filtered to only include the Warriors, Rockets, Raptors, and Celtics (note the filterbar at the top as well as the change in scale/values): I've implemented a special filter value called All!:  When the 'All!' value is selected, it doesn't matter what else is selected or displayed - this value performs a "Select All" operation. In other words, the 'All!' value overrides everything else and the charts will display every result. Notice on this next picture both the filterbar, and the fact that the chart data is exactly the same as the first picture which indicates the filter successfully returns all values: So how is this done?  First, let's go over the filter used to create the 'nba_team' filter.  select distinct team_name from [nba_shots] order by 1 Pretty straightforward. We're going to now add an 'All' value to this query using a union: select 'All!' union all select distinct team_name from [nba_shots] order by 1 Now here's the trick - we're going to look for this value in the SQL itself using the  STRPOS  function as well as some CASE WHEN logic in the WHERE clause.  Here's a picture of my full query for the "Shot Types" chart:   Like other filters, we implement the query logic into the WHERE clause. In this case the NBA filter is a  direct replacement filter  so we'll call it in square brackets, with single quotes: '[nba_team]'    where case when strpos('[nba_team|All!]', 'All!') > 0 then 1 = 1 else [team_name=nba_team] end Let's break this down. The Strpos searches for the term 'All!' in the list of filter values provided - if it finds a match, the query evaluates 1=1; this will return every values. On the other hand, if the 'All!' filter is not selected, then we use a regular [team_name=nba_team] filter.  Here is what the query evaluates to and what is sent to the underlying Redshift database:  case when strpos('All!, Golden State Warriors, Boston Celtics, Toronto Raptors, Houston Rockets', 'All!') > 0 then 1 = 1 else (team_name IN ('All!','Golden State Warriors','Boston Celtics','Toronto Raptors','Houston Rockets')) end You may be wondering why this filter was called 'All!' instead of 'all'. The reason is that strpos() searches through all values and within the entire string(s), and we don't want a hypothetical filter value of the Allentown Tigers to trigger the 'All' response. The name doesn't matter, it could be called '!All' or '!All!' - I just wanted for it to appear at the top of my list, which is sorted alphabetically.  But What If I'm Matching on ID Instead? Good, you should be matching on ID whenever possible.  Let's say my filter query instead looked like  select distinct team_id, team_name from [nba_shots] order by 1 and we match on the team_id.  Our All! filter will look like the following (since we're matching on ID, you can simply call the display value 'All') :  select -1 as team_id, 'All' as team_name union all select distinct team_id, team_name from [nba_shots] order by 1 And your WHERE clause will now look like this: where case when '[nba_team|-1]'= '-1' then 1 = 1 else [team_name=nba_team] end Hope this guide helps you create your own user-friendly Select All filters! If you have any questions about the methods used here, let me know in the comments! 

      intapiuser
      intapiuserPosted 3 years ago • Last reply 2 years ago
      1
               
      • CDTChevronRightIcon

      Alerts In Slack

               

      Sometimes users prefer to receive alerts via Slack channel as opposed to email.  While alerts are only built for email, we can receive alerts in Slack using Slack's ' Email ' app.  1. Click 'Add Configuration'  2. Choose the Slack channel you want to send alerts to from the drop down, or create a new channel, and then click 'Add Email Integration' The app works by assigning an email address to the Slack channel. We can then take that email and add it as the recipient for our alert in Sisense for Cloud Data Teams.  Easy as that! 

      intapiuser
      intapiuserPosted 3 years ago • Last reply 2 years ago
      1
               
      • CDTChevronRightIcon

      Top 4 Reasons Your View Isn't Materializing

               

      Ever have a  SQL view  that won't seem to materialize? Getting that dreaded red X even when results are showing in the View editor? Well follow the below instructions to turn those red X's into green checks! 1. YOUR SQL SYNTAX ISN'T REDSHIFT COMPATIBLE.   If your SQL query isn't able to run on the cache, it will still try to run on your database to produce results. So if you have a database that doesn't use Redshift, the query will still show results even though the syntax can't be materialized. The best solution for this is to follow proper  Redshift syntax.  If you’re working on converting MySQL syntax to Redshift, check out  this blog post  for more details. 2. THE UNDERLYING TABLES HAVE YET TO BE CACHED. Similar to point 1, your SQL query will show results if it can work on your database, but not the cache. If you're querying a table that isn’t on the cache, you might see results, but this won’t materialize! If you get the below error, chances are this is your issue: ERROR: relation "_" does not exist ERROR: relation "_" does not exist The steps to solving this are to check your cache setting page and look for the underlying tables. If you seen green checkmarks there, you should be good to go. Otherwise, go ahead and cache the table to get this view materialized. 3. TWO COLUMNS HAVE THE SAME NAME. Tables and Views on the cache require unique column names in order to materialize properly. Sometimes when joining tables non-unique column names like “id” will cause this error. Simply changing a column name to a more specific alias will do the trick here.  4. THERE ARE FILTERS IN THE VIEW. One of the biggest reasons Views fail during materialization is due to the use of filters in the SQL query. If you're using filters, you might see either of the below errors: ERROR: syntax error at or near "." ERROR: syntax error at or near "=" ERROR: syntax error at or near "." ERROR: syntax error at or near "=" Materialization doesn’t work in this scenario because filters are meant for dynamic changes to data, where as materialization is meant to provide faster, static data for you to work from. The easiest way to resolve this is to replace your filters with static clauses, or just remove these filters all together.

      intapiuser
      intapiuserPosted 3 years ago
      0
               
      • CDTChevronRightIcon

      Weighted Vs Unweighted Averages

               

      When summarizing statistics across multiple categories, analysts often have to decide between using weighted and unweighted averages.   An  unweighted average  is essentially your familiar method of taking the mean. Let's say 0% of users logged into my site on Day 1, and 100% of users logged in on Day 2. The unweighted average for the 2 days combined would be (0% + 100%)/2 = 50%.   Weighted averages  take the sample size into consideration. Let's say in the example above, there was only 1 user enrolled on Day 1 and 4 users enrolled on Day 2 - making a total of 5 users over the 2 days. The weighted average is 0% * (1/5) + 100% * (4/5) = 80%. Typically, users want to calculate weighted averages as it prevents skewing from categories with smaller sample sizes.   If we want to add a row with a weighted average, we can accomplish this via SQL as shown in the example below (note that this example leverages Redshift syntax).    Let's walk through an example of how to calculate each of these measures! Let's say our original table, t1, contains the following data: Here is how to calculate the weighted average. To add an extra 'Total' row, I used a SQL Union all. select month::varchar(12) , (round(perc_purchases_over_10_dollars * 100, 2) || '%') as perc_purchases_over_10_dollars , sample_size from t1 union all select 'weighted avg' , (round(sum(perc_purchases_over_10_dollars * sample_size) / sum(sample_size) * 100, 2) || '%') , sum(sample_size) from t1 group by 1 The result looks like this: On the contrary, if we would prefer to use an unweighted average, we can simply union an avg() of each of the categories.  (The additional round/decimal casting is for formatting purposes.) select month::varchar(15) , (round(perc_purchases_over_10_dollars * 100, 2) || '%') as perc_purchases_over_10_dollars , sample_size from t1 union all select 'unweighted avg' , (round(avg(perc_purchases_over_10_dollars) * 100, 2)::decimal(6,2) || '%') , sum(sample_size) as sample_size from t1 Here, we can see that the results differ from the weighted average example (12.04% as opposed to 12.00%). Note that unweighted and weighted averages are equal if each category has the same sample size. ***Interested in learning more about adding a 'Total' Row or Column? Check out our community post  here .***

      intapiuser
      intapiuserPosted 3 years ago
      0
               
    …