Custom Report Generation

More
4 years 4 months ago #108461 by KitchM
KitchM created the topic: Custom Report Generation
Because the reports I can find in the program are fairly useless to me, I need a custom report that shows the results from my election poll in the format I can use. Is there an add-on that creates custom reports? Is there any way to do this in any case?

Thanks.

Please Log in or Create an account to join the conversation.

More
4 years 4 months ago #108517 by jelo
jelo replied the topic: Custom Report Generation
Export Data and use external software is the way to go. Most of the time the data needs to be checked and cleaned too. So don't expect AddOns in the reporting section. I find the report tool quite powerful. What is missing?

Are you a student conducting a survey? If yes, tell me why you use LimeSurvey?
www.limesurvey.org/forum/development/116...y-you-use-limesurvey

Please Log in or Create an account to join the conversation.

More
4 years 4 months ago #108524 by tammo
tammo replied the topic: Custom Report Generation
We also have been searching a good report generator, since our needs were complicated we decided to write one ourselves.

One of the results, based on a Limesurvey questionnaire (in Dutch, sorry) is here:
boomenbos.toolsforresearch.com/tfr/ishc.php?savedid=61

The software we have written for this is not open source.


Tammo ter Hark at Tools for Research
Proud developers of the TFR Responsive Limesurvey template
toolsforresearch.com/limesurvey-responsive-template
The following user(s) said Thank You: Ben_V

Please Log in or Create an account to join the conversation.

More
4 years 4 months ago #108526 by jelo
jelo replied the topic: Custom Report Generation
Very nice. Looks easy, but hard work. What is the technical approach.

Direct query of Limesurvey database?
Or export of Limesurvey results and import in your webbased reporting tool?

I wonder if LS 2.05 would allow you to offer a reporting addon for the commercial space?

BTW: Added "Kleine Stappen" to my very small collection of dutch words ;-)

Are you a student conducting a survey? If yes, tell me why you use LimeSurvey?
www.limesurvey.org/forum/development/116...y-you-use-limesurvey

Please Log in or Create an account to join the conversation.

More
4 years 4 months ago #108527 by tammo
tammo replied the topic: Custom Report Generation
We did this using javascript, based on querying the limesurvey database, so real time reports are also possible. We also used this for a survey with now about 28.000 complete responses.

Also see: Demo.gewoongoedwerkmeter.nl, which generates a report at the end (after many mandatory questions about how employees rate their employer). The Demo prefix makes it easy to participate without registering and without storing the Demo-answers in the actual database.

It was done on basis of version 1.92, we are now planning the migration to 2.05 this summer.

Limesurvey is great, but left us amazed at certain moments. We are now developing a fully responsive theme design.


Tammo ter Hark at Tools for Research
Proud developers of the TFR Responsive Limesurvey template
toolsforresearch.com/limesurvey-responsive-template

Please Log in or Create an account to join the conversation.

More
1 year 5 months ago - 1 year 5 months ago #152495 by Kevin
Kevin replied the topic: Custom Report Generation
This took me a week to complete because I'm not a database person. It should help people produce reports without exporting the data to excel and creating a pivot table.

CREATE DEFINER=`A_VALID_USER_NAME_HERE`@`localhost` PROCEDURE `usp_produce_survey_report`(IN survey_id VARCHAR(10), IN lang VARCHAR(2))
    SQL SECURITY INVOKER
BEGIN
 
/*---------------------------------------------------------------------------------
I do not guarantee that this will work for you or that it cannot be hacked with
with SQL injections or other malicious intents. 
 
This stored procedure will produce output that you may use to create a report.
It accepts two arguments; The survey id (745) and the language (en).
It parses the column name in the survey table to get the qid.
 
It will copy the answers from the survey table to the survey_report
table if the answer is type S or K. It will get the answers from
the answers table for other types. NOTE: Other types might need to
be added to the if statement.
 
Additionally, the qid and id from the survey table are also copied to
the survey_report table.
 
Then the questions from the questions table, and answers from the answers
and survey_report tables are combined and displayed.  
 
The data in the survey_report table is deleted after the data is displayed.
 
The id from the survey table is displayed as the respondent_id which may
be used to combine the questions and answers from a specific respondent.  
 
You may have to change the prefix on the table names.
Example: survey_answers to my_prefix_answers.
 
Use this to call the procedure.
Syntax:  call survey.usp_produce_survey_report('<SURVERY_ID>', '<LANGUAGE>');
Example: call survey.usp_produce_survey_report('457345', 'en'); 
 
use this to create the table that stores the data
 CREATE TABLE `survey_report` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `qid` int(11) NOT NULL DEFAULT '0',
    `survey_row_id` int(11) NOT NULL DEFAULT '0' COMMENT 'id that is in the survey_<id> table',
    `answer` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
   PRIMARY KEY (`id`)
);
*/
 
DECLARE v_col_name VARCHAR (25);
DECLARE v_qid INT;
DECLARE v_col_count INT DEFAULT 0;
DECLARE done INT DEFAULT false;
DECLARE tname VARCHAR(24) DEFAULT CONCAT('survey_survey_',survey_id);
DECLARE counter INT DEFAULT 0;
DECLARE x INT DEFAULT 0;
 
-- select locate ('X','123457X212X1125', 8);  -- use locate to determine location of second X - returns 11
-- select substring('123457X212X1125', 11+1, 7); -- use substring to get the qid - returns 1125
 
DECLARE cur1 cursor for
    SELECT column_name, substring(column_name, 11+1, 7) as qid -- get the qid from the column name.  the 7 might need to be higher depending on the id.  
       FROM information_schema.columns -- this has the column names
       WHERE table_name = tname -- table name created from the id that was passed to the stored procedure
       AND column_name REGEXP 'X'; -- get the columns that have an X
 
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
SET done = FALSE;
 
OPEN cur1;
SELECT FOUND_ROWS() INTO v_col_count; -- Get number of column names
   -- SELECT v_col_count; -- print the number of column names
   read_loop: LOOP
      FETCH cur1 INTO v_col_name, v_qid; -- v_col_name is the original column name and v_qid is the qid that is taken from the column name
 
      IF done THEN
         LEAVE read_loop;
      END IF;
      -- SELECT v_col_name, v_qid;   
      SET counter = 1; -- use to compare id's
      SET x = 1; -- used for the while loop
 
      WHILE x < v_col_count DO
         SET @sql := NULL;
         -- SELECT v_col_name, v_qid, counter, x; 
         -- SELECT counter as id, v_col_name, v_qid as qid, x; 	  
         -- SET @sql = CONCAT ('SELECT id ', ',',v_qid, ' as qid ,', v_col_name,' FROM ', tname, ' WHERE id = ', counter );
         -- I would have to join the survey table below if I did not add the answer (v_col_name). I assume this is faster than another join.
         SET @sql = CONCAT ('INSERT INTO survey_report(qid,survey_row_id,answer) SELECT ',v_qid, ',id,' , v_col_name, '  FROM ', tname, ' WHERE id = ', counter );
 
         -- SELECT @sql;       
         PREPARE stmt FROM @sql;
         EXECUTE stmt;
         DEALLOCATE PREPARE stmt;
         -- SELECT counter, x; 
         SET x = x + 1; -- increment counter for while loop
         SET counter = counter + 1; -- increment counter for id's
      END WHILE;
   END LOOP; -- read_loop
   CLOSE cur1;
 
   -- SELECT * FROM survey_report
   -- ORDER BY id, qid;
 
   SET @counter = 0;
 
   SELECT 
      -- @counter:=@counter + 1 AS newindex, -- increment the counter that is in the header
      -- survey_report.id AS id,
      survey_report.survey_row_id as respondent_id, -- the id that copied from the survey table
      -- survey_report.qid,
      question,
      IF(type IN ('S' , 'K'),
         (SELECT answer
            FROM survey_report
            WHERE qid NOT IN (SELECT qid
                    FROM survey_answers)
                    AND id = @counter),
        (SELECT answer
            FROM survey_answers
            WHERE survey_questions.qid = survey_answers.qid
                    AND survey_report.qid = survey_questions.qid
                    AND survey_report.answer = survey_answers.code
                    AND survey_answers.language = lang)) AS answer
            FROM survey_questions
                    JOIN survey_report ON survey_report.qid = survey_questions.qid
                    WHERE survey_questions.sid = survey_id
					   AND survey_questions.language = lang
                    ORDER BY survey_report.survey_row_id;
 
   TRUNCATE TABLE survey_report;    
END
Last Edit: 1 year 5 months ago by Kevin.

Please Log in or Create an account to join the conversation.

More
1 year 5 months ago - 1 year 5 months ago #152500 by Kevin
Kevin replied the topic: Custom Report Generation
The solution returned 2,838 rows (43 respondents) but the original survey table has 28,994 rows and respondents. I did not get an error but I wonder if there is an issue with memory? The tested survey table has 44 columns for answers for approximately 33 questions.

It worked ok on a previous test with only a few questions and answers.
Last Edit: 1 year 5 months ago by Kevin.

Please Log in or Create an account to join the conversation.

More
1 year 5 months ago - 1 year 5 months ago #152518 by Kevin
Kevin replied the topic: Custom Report Generation
CREATE DEFINER=`some_user`@`localhost` PROCEDURE `usp_produce_survey_report`(IN survey_id VARCHAR(10), IN lang VARCHAR(2))
    SQL SECURITY INVOKER
BEGIN
 
/*---------------------------------------------------------------------------------
I do not guarantee that this will work for you or that it cannot be hacked with
with SQL injections or other malicious intents. 
 
This stored procedure will produce output that you may use to create a report.
It accepts two arguments; The survey id (745) and the language (en).
It parses the column name in the survey table to get the qid.
 
It will copy the answers from the survey table to the survey_report
table if the answer is type S or K. It will get the answers from
the answers table for other types. NOTE: Other types might need to
be added to the if statement.
 
Additionally, the qid and id from the survey table are also copied to
the survey_report table.
 
Then the questions from the questions table, and answers from the answers
and survey_report tables are combined and displayed.  
 
The data in the survey_report table is deleted after the data is displayed.
 
The id from the survey table is displayed as the respondent_id which may
be used to combine the questions and answers from a specific respondent.  
 
You may have to change the prefix on the table names.
Example: survey_answers to my_prefix_answers.
 
Use this to call the procedure.
Syntax:  call survey.usp_produce_survey_report('<SURVERY_ID>', '<LANGUAGE>');
Example: call survey.usp_produce_survey_report('457345', 'en'); 
 
use this to create the table that stores the data
 CREATE TABLE `survey_report` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `qid` int(11) NOT NULL DEFAULT '0',
    `survey_row_id` int(11) NOT NULL DEFAULT '0' COMMENT 'id that is in the survey_<id> table',
    `answer` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
   PRIMARY KEY (`id`)
);
*/
 
DECLARE v_col_name VARCHAR (25);
DECLARE v_qid INT;
DECLARE v_col_count INT DEFAULT 0;
DECLARE done INT DEFAULT false;
DECLARE tname VARCHAR(24) DEFAULT CONCAT('survey_survey_',survey_id);
DECLARE counter INT DEFAULT 0;
DECLARE x INT DEFAULT 0;
DECLARE r_count INT DEFAULT 0;
 
-- select locate ('X','123457X212X1125', 8);  -- use locate to determine location of second X - returns 11
-- select substring('123457X212X1125', 11+1, 7); -- use substring to get the qid - returns 1125
 
DECLARE cur1 cursor for
    SELECT column_name, substring(column_name, 11+1, 7) as qid -- get the qid from the column name.  the 7 might need to be higher depending on the id.  
       FROM information_schema.columns -- this has the column names
       WHERE table_name = tname -- table name created form the id that was passed to the stored procedure
       AND column_name REGEXP 'X'; -- get the columns that have an X
 
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
SET done = FALSE;
 
   OPEN cur1;
   -- SELECT FOUND_ROWS() INTO v_col_count; -- Get number of column names
   -- SELECT v_col_count; -- print the number of column names
   SET r_count = (SELECT COUNT(*) FROM survey_survey_144477);  
   -- SELECT r_count;
 
   read_loop: LOOP
      FETCH cur1 INTO v_col_name, v_qid; -- v_col_name is the original column name and v_qid is the qid that is taken from the column name
 
      IF done THEN
         LEAVE read_loop;
      END IF;
      -- SELECT v_col_name, v_qid;   
      SET counter = 1; -- use to compare id's
      SET x = 1; -- used for the while loop
 
      WHILE x <= r_count DO
         SET @sql := NULL;
         -- SELECT v_col_name, v_qid, counter, x; 
         -- SELECT counter as id, v_col_name, v_qid as qid, x;    
         -- SET @sql = CONCAT ('SELECT id ', ',',v_qid, ' as qid ,', v_col_name,' FROM ', tname, ' WHERE id = ', counter );
         -- I would have to join the survey table below if I did not add the answer (v_col_name). I assume this is faster than another join.
         SET @sql = CONCAT ('INSERT INTO survey_report(qid,survey_row_id,answer) SELECT ',v_qid, ',id,' , v_col_name, '  FROM ', tname, ' WHERE id = ', counter );
 
         -- SELECT @sql;       
         PREPARE stmt FROM @sql;
         EXECUTE stmt;
         DEALLOCATE PREPARE stmt;
         -- SELECT counter, x; 
         SET x = x + 1; -- increment counter for while loop
         SET counter = counter + 1; -- increment counter for id's
      END WHILE;
   END LOOP; -- read_loop
   CLOSE cur1;
 
   -- SELECT * FROM survey_report
   -- ORDER BY id, qid;
 
   SET @counter = 0;
 
   SELECT 
      @counter:=@counter + 1 AS newindex, -- increment the counter that is in the header
      survey_report.id,
      survey_report.survey_row_id as respondent_id, -- the id that copied from the survey table
      survey_report.qid,
      question,
      IF(type IN ('S' , 'K'),
         (SELECT answer
            FROM survey_report
            WHERE qid NOT IN (SELECT qid FROM survey_answers)
            AND survey_questions.language = lang
                  AND survey_report.id = @counter),
         (SELECT answer
            FROM survey_answers
            WHERE survey_questions.qid = survey_answers.qid
                  AND survey_report.qid = survey_questions.qid
                  AND survey_report.answer = survey_answers.code
                  AND survey_answers.language = lang
             )
          ) AS answer
       FROM survey_questions
          JOIN survey_report ON survey_report.qid = survey_questions.qid
          WHERE survey_questions.sid = survey_id
          ORDER BY survey_report.survey_row_id, survey_report.id;
 
   TRUNCATE TABLE survey_report;    
END
Last Edit: 1 year 5 months ago by Kevin. Reason: Changed the while loop variable and the location of the language condition.

Please Log in or Create an account to join the conversation.

More
1 year 5 months ago #152523 by Kevin
Kevin replied the topic: Custom Report Generation
I'm posting this again because I cannot edit the previous post. I got the number of rows from the schema and changed the loop variables.
CREATE DEFINER=`some_user`@`localhost` PROCEDURE `usp_produce_survey_report`(IN survey_id VARCHAR(10), IN lang VARCHAR(2))
    SQL SECURITY INVOKER
BEGIN
 
/*---------------------------------------------------------------------------------
I do not guarantee that this will work for you or that it cannot be hacked with
with SQL injections or other malicious intents. 
 
This stored procedure will produce output that you may use to create a report.
It accepts two arguments; The survey id (745) and the language (en).
It parses the column name in the survey table to get the qid.
 
It will copy the answers from the survey table to the survey_report
table if the answer is type S or K. It will get the answers from
the answers table for other types. NOTE: Other types might need to
be added to the if statement.
 
Additionally, the qid and id from the survey table are also copied to
the survey_report table.
 
Then the questions from the questions table, and answers from the answers
and survey_report tables are combined and displayed.  
 
The data in the survey_report table is deleted after the data is displayed.
 
The id from the survey table is displayed as the respondent_id which may
be used to combine the questions and answers from a specific respondent.  
 
You may have to change the prefix on the table names.
Example: survey_answers to my_prefix_answers.
 
Use this to call the procedure.
Syntax:  call survey.usp_produce_survey_report('<SURVERY_ID>', '<LANGUAGE>');
Example: call survey.usp_produce_survey_report('457345', 'en'); 
 
use this to create the table that stores the data
 CREATE TABLE `survey_report` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `qid` int(11) NOT NULL DEFAULT '0',
    `survey_row_id` int(11) NOT NULL DEFAULT '0' COMMENT 'id that is in the survey_<id> table',
    `answer` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
   PRIMARY KEY (`id`)
);
*/
 
DECLARE v_col_name VARCHAR (25);
DECLARE v_qid INT;
DECLARE v_col_count INT DEFAULT 0;
DECLARE done INT DEFAULT false;
DECLARE tname VARCHAR(24) DEFAULT CONCAT('survey_survey_',survey_id);
DECLARE counter INT DEFAULT 0;
DECLARE current_row INT DEFAULT 0;
DECLARE total_rows INT DEFAULT 0;
 
-- select locate ('X','123457X212X1125', 8);  -- use locate to determine location of second X - returns 11
-- select substring('123457X212X1125', 11+1, 7); -- use substring to get the qid - returns 1125
 
DECLARE cur1 cursor for
    SELECT column_name, substring(column_name, 11+1, 7) as qid -- get the qid from the column name.  the 7 might need to be higher depending on the id.  
       FROM information_schema.columns -- this has the column names
       WHERE table_name = tname -- table name created form the id that was passed to the stored procedure
       AND column_name REGEXP 'X'; -- get the columns that have an X
 
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
SET done = FALSE;
 
OPEN cur1;
 
   SET total_rows = (SELECT table_rows -- get the number of rows
      FROM INFORMATION_SCHEMA.TABLES
      WHERE table_name = tname);
   -- SELECT total_rows;
 
   read_loop: LOOP
      FETCH cur1 INTO v_col_name, v_qid; -- v_col_name is the original column name and v_qid is the qid that is taken from the column name
 
      IF done THEN
         LEAVE read_loop;
      END IF;
      -- SELECT v_col_name, v_qid;   
      SET counter = 1; -- use to compare id's
      SET current_row = 1; -- used for the while loop
 
      WHILE current_row <= total_rows DO
         SET @sql := NULL;
         -- SELECT v_col_name, v_qid, counter, x; 
         -- SELECT counter as id, v_col_name, v_qid as qid, x;    
         -- SET @sql = CONCAT ('SELECT id ', ',',v_qid, ' as qid ,', v_col_name,' FROM ', tname, ' WHERE id = ', counter );
         -- I would have to join the survey table below if I did not add the answer (v_col_name). I assume this is faster than another join.
         SET @sql = CONCAT ('INSERT INTO survey_report(qid,survey_row_id,answer) SELECT ',v_qid, ',id,' , v_col_name, '  FROM ', tname, ' WHERE id = ', counter );
 
         -- SELECT @sql;       
         PREPARE stmt FROM @sql;
         EXECUTE stmt;
         DEALLOCATE PREPARE stmt;
         -- SELECT counter, x; 
         SET current_row = current_row + 1; -- increment counter for while loop
         SET counter = counter + 1; -- increment counter for id's
      END WHILE;
   END LOOP; -- read_loop
   CLOSE cur1;
 
   -- SELECT * FROM survey_report
   -- ORDER BY id, qid;
 
   SET @counter = 0;
 
   SELECT 
      @counter:=@counter + 1 AS newindex, -- increment the counter that is in the header
      survey_report.id,
      survey_report.survey_row_id as respondent_id, -- the id that copied from the survey table
      survey_report.qid,
      question,
      IF(type IN ('S' , 'K'),
         (SELECT answer
            FROM survey_report
            WHERE qid NOT IN (SELECT qid FROM survey_answers)
            AND survey_questions.language = lang
                  AND survey_report.id = @counter),
         (SELECT answer
            FROM survey_answers
            WHERE survey_questions.qid = survey_answers.qid
                  AND survey_report.qid = survey_questions.qid
                  AND survey_report.answer = survey_answers.code
                  AND survey_answers.language = lang
             )
          ) AS answer
       FROM survey_questions
          JOIN survey_report ON survey_report.qid = survey_questions.qid
          WHERE survey_questions.sid = survey_id
          ORDER BY survey_report.survey_row_id, survey_report.id;
 
   TRUNCATE TABLE survey_report;    
END

Please Log in or Create an account to join the conversation.

More
4 months 2 weeks ago #168081 by r0bis
r0bis replied the topic: Custom Report Generation
In my experience the best way to generate reports quickly is to use R and knitr. There is an R libray on github called limer which can esily be installed into R. And it would make much sense to use Rstudio for working in this environment. This is incredibly powerful, you can write reports in R markdown and during the generation process limer connects to your survey via the official Limesurvey API, gets your data in seconds and output with all summarisations of data and publication quality graphs (via ggplot in R) are produced. Output can be html, MS Word, PDF or even markdown. It requires of course to familiarise oneself with R, but there are so many free and excellent courses available on MOOCs for that. If you issue knit command (press a button in Rstudio) pdf output from your data on the internet is created in maybe 10 seconds.
The following user(s) said Thank You: LouisGac

Please Log in or Create an account to join the conversation.

More
4 months 2 weeks ago #168167 by holch
holch replied the topic: Custom Report Generation
Thanks rObis for your insights. Unfortunately R has a steep learning curve, which has kept me away until now. I even started a course on some of the MOOCs, but you know how things go. There are projects coming in between, suddenly you are behind, etc. And often the courses are very general and creating reports is not touched or only very superficial. Do you have any specific recommendation for a course that covers this topic well? I would love to have better dynamic reports for certain studies.

I'm not a LimeSurvey GmbH member. I answer at the LimeSurvey forum in my spare time. No support via private message.
Some helpful links: Manual (EN) | Question Types | Workarounds

Please Log in or Create an account to join the conversation.

More
4 months 2 weeks ago - 4 months 2 weeks ago #168349 by r0bis
r0bis replied the topic: Custom Report Generation
Hi Holch, I think this is an important issue so I waited till I have some time to put together a fuller answer.

1. First steps
First of all with R it is important to get a dataset you are really interested in, get it inside R and play around. After you install Rstudio (makes little sense not to make use of it when it is freely available) the first task really is to read in data (from CSV or XLSX whatever), use some commands to get used to dataframes - e.g. how to subset how address any value by it's index (and names) e.g. columns (variables) can easily be seen as myDataFrame$myColumn. You need to get intuitively a little familiar with vectorised approach which seems really easy after you are able to say multiply one column to another - e.g. myFr$Col1 * myFr$Col2 gives another vector. Datafame essentially consists of vectors (columns/variables) aligned together. Variables can mainly be of types character, numeric,date,factor. Once you are sligtly familiar with this - you can start visualising data with ggplot2. Of course - you can build plots quite easily with the built in graphics, but ggplot2 is so much more attractive - it gives instant publication quality graphs. Then you start putting plots together in a report with knitr - that gives you the possibility to write your reports in markdown, inserting pieces of code in the text - like ... From this study number of respondents today is `r nrow(myDataFr)` and average score is `r mean(myDataFr$score)` .... and in addition you can place your publication quality graphs throughout the report. Or in tabular format as well. Output can be any of your choosing - html, PDF or MSWord - every time you run it it will be assembled from your latest data. Rstudio has good use of screen space and makes for better access to the built in help system - just type ?command in the console and the help will be displayed in one of the 4 windows that Rstudio displays.

2. Courses and other means of learning
I started off with MITx Analytics Edge and it was really interesting. However more specific to R is Coursera Data Science specialisation - I've done the first four steps there. But for reports as such I really think that DataCamp has ggplot specific courses - they are the ones that will give better insight in how visualisation happens. All of those are with hands one exercises. Then hugely helpful is stackexchange. Almost every R data wrangling and plotting problem has been answered there and people are really helpful with this.

3. Why it is important
Well, R has become lingua franca in data analysis. Even though there are other programs available, this is the one that is taught in every university and mose every data analyst is aware of it. It has enthusiastic user base and because of its embeddedness in universities for almost every imaginable analysis or modelling problem there is a good standard software package out there. To get it is as simple as typing `install.packages("myLibraryOfInterest") - R goes to the nearest repository and gets that package inside your R. You load it with library(myLibraryOfInterest) and you are free to peruse it's commands and help files.

4. What limer does and more
It simply gets the data from a specific survey inside your R as a dataframe. You can do any analysis and or visualisation you like, using the above process.

But R of course also offers libraries that allow to manipulate data through SQL or to even connect to SQL servers directly. If you like, you can also build interactive visualizations via another offshoot of Rstudio - "Shiny". I have not done the latter, but the beauty of it all is - you do not need to have it all at once, you can take from R piece by piece and use it immediately. There will be lots of people on stackexchange to help you if you get stuck.

P.S. In addition a good page to go through about ggplot is tutorials.iq.harvard.edu/R/Rgraphics/Rgraphics.html - it requires no additional software, the dataset is distributed with R, so you can simply read and copy paste the code in your R to observe the results.
Last Edit: 4 months 2 weeks ago by r0bis. Reason: addition of a reference

Please Log in or Create an account to join the conversation.

More
4 months 1 week ago #168381 by holch
holch replied the topic: Custom Report Generation
Thank you for your detailed post. I will have to sit my *ss down to get into this I think.

I'm not a LimeSurvey GmbH member. I answer at the LimeSurvey forum in my spare time. No support via private message.
Some helpful links: Manual (EN) | Question Types | Workarounds

Please Log in or Create an account to join the conversation.

Start now!

Just create your account and start using Limesurvey today.

Register now
Join our Newsletter!