Data-Diggers.com | Zen Cart Optimization, Data Mining and Performance Modules for Zen Cart

TAG | Zen Cart

Randomized tests version 1.3 is here (download). This release brings quite few new features that should be very helpful. I added 95% trust intervals for each statistic. There’s also probability that given experiment will beat control group. You can now also decide when impression occurs. Read below for more.

Installation

Refer to INSTALL.txt in .zip file.

If You already have RT installed refer to INSTALL.txt file for UPGRADE instructions.

Trust intervals

Randomized Tests provides few estimates:

  • order conversion rate
  • visitor to customer conversion rate
  • total items bought
  • total order value

You have to remember that RT presents only estimated values – for example after tracking ten orders RT might calculate that order conversion rate is 0.9%, but You’re almost sure (because You know that from earlier experiments) that Your conversion rate is 1.1%. Unfortunately RT can’t do much better since it hasn’t collected enough data yet. In new version RT will however present also 95% chance intervals for each estimated value. So now instead of conversion rate 0.9% You’ll see 0.9% +/- 0.2% which means that there’s 95% chance that true conversion rate is between 1.1% and 0.7%.

Chance to beat Control Group

Previous versions of RT leaved to You decision if changes You made were actually useful. In version 1.3 RT will give You probability that those changes outpeform original version of Your Zen Cart store. RT displays now ‘Chance to Beat Original’ value. This value should be around 95% or 5% before You decide that new version perform better/worse then original version of Your store.

Custom impression detection functions

This new feature lets You decide if and when to include particular visitor in experiment. You might want for example test performance of Your new customer registration form and include in experiment only those visitors who actually visited customer registration form. Thanks to that You’ll get more accurate results very quickly.

There will be separate post about this new feature – so stay tuned ;)

Download

You can download latests release of RT here:

www.data-diggers.com/contribs/rt/downloads/rt.zip

, ,

Yeah, I’m not dead. I don’t have much time but I still work on my modules. I spent most time on Randomized Tests module – sorry to all people counting on new Query Cache version ;) . In upcoming release of Randomized Tests You’ll be able to:

  • decide whether or not impression occurred (and when it happened). For example, You want to count in experiment only visitors that viewed ‘login’ page. Now You can :)
  • get more accurate results. Experiment stats view in Admin area will give You probability that given test group will beat control group. All stats will be also presented with 95% probability range. For example visitor/customer conversion ratio will be presented as: 1% +/- 0.02% which mean that conversion ratio is in [0.98%, 1.02%] with 95% probability.
  • maybe more… for example support for Your own statistics (like ’search form usage stats’, ‘contact us page stats’ etc).

Would You like to see more features in next version? Let me know through comments / mail.

Sorry, multivariate testing will probably not be included in upcoming version. I’ll add support for it most probably in next next version.

, ,

New version of Randomized Tests for Zen Cart is available. New version introduces few bug fixes and one very important feature – orders filters and customer filter. Order/Customer filters can be used to dynamically remove (not permanently) from statistics certain customers/orders and recalculate statistics without them. Why is it important?

Let’s assume that You have store with many customers. You use Randomized Tests to check how new layout affects sales. Unfortunately after You run Your experiment for few days You notice that one group is biased because it includes one or two top buyers. Those two buyers are different from regular customer – they buy a lot of items, place orders every few days and probably aren’t affected by new layout – they already know Your store and don’t care about it as long as they can place orders.

Group containing these customers is biased since it includes data outliers. You probably would like to remove them from statistics. New version of Randomized Tests can do that using filter functions. Currently there are two types of filter functions:

  • order filter functions – executed for each order in group – allows removal certain orders from statistics.
  • customer filter functions – executed for each customer in group. Orders from rejected customers will not included in statistics.

Randomized Tests V1.2 contain two sample filter functions:

  • ddigers_2std_customer_total_rt_cff() – removes top buyers (formally – customers with total spent in 2+ standard deviation from average) from statistics.
  • ddigers_2std_order_total_rt_off() – removes super big orders from statistics.

To check how they’re implemented view admin/includes/functions/extra_functions/randomized_tests.php

You can implement Your own functions by following below pattern.

Custom Customer Filter Functions

Randomized Tests will recognize any function with name ending with “_rt_cff” as customer filter function and allow You to use it. For example following function will be recognized as filter:

function customer_filter_rt_cff($customer_id) {
if($cid == 12345) return(FALSE);
else return(TRUE);
}

Randomized Tests passes to customer filter functions single attribute – id of customer in database. If function returns TRUE customer is accepted in calculation of statistics, and is rejected otherwise.

Order Filter Functions

Order filter functions are similar to customer filter functions – any function with name ending with “_rt_off” will be recognized as order filter function. Example filter function:

function order_filter_rt_off($eid, $gid, $cid, $oid, $itemsBought, $ordersTotal) {
if($orderTotal > 1000.0) return(FALSE);
else return(TRUE);
}

Randomized Tests passes few more parameters to order filter functions:

  • $eid – experiment row id in database
  • $gid – group row id in database
  • $cid – customer id in database
  • $oid – order id in database
  • $itemsBought – number of items in order
  • $totalValue – total value of order

As with customer filter functions order filter functions should return TRUE to accept order in statistics and FALSE to reject it.

Making Filter Functions Visible to Randomized Tests Module

Let’s assume that You’ve placed Your custom filter functions in admin/includes/functions/extra_functions/my_custom_filters.php file. The best way to make them visible to RandomizedTests is to include the file through autoloaders interface. Create config.filters.php file in admin/includes/auto_loaders/ and paste there:

$autoLoadConfig[180][] = array(‘autoType’=>‘require’,
‘loadFile’=> DIR_FS_CATALOG . DIR_WS_FUNCTIONS . ‘extra_functions/my_custom_filters.php’);

That’s it – Randomized Tests module will now be able to use Your filters.

Screenshot

Here’s screenshot of experiment with applied filters:

Download

You can download current version of Randomized Tests here.

, ,

This module allows You to perform experiments on Your website. You can test for example how Your new ‘Add to Cart’ button performs compared to old one.

I don’t have much time to write long post, so I’ll cut it to most important things. You can download the module here. This module requires UTI to work properly (download it here ). Installation instructions are in .zip file. Here’s how You can use it.

To create experiment follow these steps:

  1. Go to Admin->Tools->[RT] Groupsets.
  2. Create groupset (it’s set of groups). Call it ‘Basic Test Groupset’.
  3. Click on Basic Test Groupset – You will be forwarded to page where You can edit it.
  4. Each groupset must have EXACTLY one control group. I usually call it ‘Control Group’. Create two groups (you can create as many groups in groupset as You want, but for simplicity We use 2 now):
  5. Control Group – with id ‘cg’ (as ControlGroup). Check ‘control group’ checkbox
  6. Test Group – with id ‘tg’. DO NOT check ‘control group’ checkbox.
  7. Go to Admin->Tools->[RT] Experiments.
  8. Create Experiment ‘Exp #1′ using groupset ‘Basic Test Groupset’.

Click on Exp #1 – You will be forwarded to its stats:

  • Impressions - in this version of Randomized Tests its simply number of visitors in each group
  • Customers Registered – (visitor to customer conversion rate) / (change in regard to control group) / (total count of newly registered customers in this group)
  • Orders Placed – (visitor to order conversion rate) / as above / (total number of orders visitors in this group placed)
  • Items Bought – (avg. size of order) / as above / (total number of items bought)
  • Orders Value – (avg. order value) / as above / (total orders value)

Ok, You’ve created experiment, now You have to create two versions of Your page. Let’s assume that You want to change ‘Add to Cart’ button on product_info page. Edit includes/templates/your_template/templates/tpl_product_info_display.php.Find code responsible for displaying ‘Add to Cart’ button – in my case it’s:

echo $display_button;

Change it to:

if($exp->groupID == ‘tg’) {
// visitor has been assigned to Test Group
echo $display_my_new_button;
} else {
// user is in Control Group – We don’t change anything
echo $display_button;
}

$exp is global variable (it’s instance of ExperimentManager defined in includes/classes/randomized_tests.php) and holds basic information about experiment and group to which has been assigned current visitor
$exp->groupID is ‘id‘ of group that You assigned in step 4. You can use it to detect in which group is current visitor and perform custom action.

You’re done. Wait until experiment becomes statistically significant ( >2000 visitors in each group, many orders ) and check results.

, , ,

UTI is tiny module that tracks visitors to Zen Cart site even if they aren’t logged in or even never created any account. It allows developers to keep in UTI memory small amounts of information about each visitor. For example, UTI can be used to store list of recently viewed products by any particular user. UTI will remember this information and when the visitor will come back to Zen Cart store week later UTI will recognize him and present list of previously viewed products.

UTI takes care of:

  • recognizing visitors – even if they never created any account
  • storing and retrieving information about them in database

UTI does not store any personal information in visitors browser.

How to use UTI

UTI is very simple class. It creates global $uti object that contains all information about current visitor. From developer point of view UTI class has four methods:

  • $uti->set($name, $value) - stores in database $value as value for attribute $name
  • $uti->get($name) – retrieves from database value for attribute $name. If there’s no such attribute method returns FALSE.
  • $uti->has($name) – checks if current $uti object has attribute $name set
  • $uti->delete($name) – deletes attribute.

$uti is automatically instantiated through auto_loaders/ mechanism. Developer can use it in any function/class since it’s global variable.

To show how easy it is to use UTI and what it can be used for I’ve created simple contributions that:

  • records and displays in sidebox list of products visitor viewed recently.
  • records and displays in sidebox list of searches visitor performed recently.
  • records when visitor changes currency and saves it as his/hers default. When visitor comes back to Zen Cart contribution automatically switches currency to most recent one.

Example contributions are included in UTI package. You can download it here: current version of UTI.

In next blog post I’ll describe how I’ve created one of those contributions using UTI.

, , ,

I’ve created User Tracking Interface (UTI) module for Zen Cart v1.3.8 which recognizes visitors (even if they are not logged in or don’t have account) and stores simple bits of information about them in database. You can use UTI to store such information as favorite currency of each visitor, recently viewed products by particular visitor or recently searched products. The best thing is that even if the visitor will leave Your site and will come back later UTI will recognize him/her and will set site to his/hers preferences.

You can test it Yourself on demo store here:

www.data-diggers.com/contribs/uti/demo-stores/with-uti/

How to test it

  1. View some products (4-6). The module will remember which items You viewed and will present them in sidebox.
  2. Search for some stuff like “mouse”, “unreal”, “mary”. The module will remember what You searched for and will present links to previous searches in sidebox.
  3. Change currency. The module will remember Your last currency and when You’ll be back next day/week/month the site will display with Your currency set.
  4. Close down Your browser and switch to different one. Visit above site again. The system will recognize You and adjust site to Your preferences.

What UTI is really for

Recently viewed products, recent search terms and user default currency are nice features, but they are just examples of use of UTI. UTI is simple class which developers can use to make user aware contributions. UTI takes care of recognizing users and storing/retrieving information about them in/from database. UTI attaches itself to Zen Cart on each request and creates $uti variable which developers can use to store simple information about visitors in database.

How to use UTI

UTI has very simple interface. Developer should use only two methods:

  • $uti->get($attribute) - retrieves visitors value for $attribute from database
  • $uti->set($attribute, $value) – store visitors value for $attribute in database

Developer does not have to worry about creating $uti object. $uti object will automatically recognize user and will retrieve all information about him from database.

What UTI can be used for

UTI is tool which can be used to create very simple contributions, for example:

  • remembering language of given visitor. On his next visit site can be automatically switched to preferred language.
  • presenting recommendations to visitor based on his previous purchases (even if visitor isn’t logged in).
  • many more.

What I need from You

Please test above site and report any bugs. You can also install current version of UTI on Your site and report any bugs. You can download current version of UTI here:

www.data-diggers.com/contribs/uti/downloads/uti-current.zip

Installation instructions are in uti-current.zip

, , ,

Jun/09

29

Speed up admin/orders.php page

There’s suboptimal query in admin/orders.php page which retrieves information about last orders. Unfortunately query is constructed in such way that MySQL scans whole orders, orders_products and orders_total table. As always it’s not a problem until orders table gets big ( for example 50 000 entries ). If You happen to have at least 10 000 orders ( I wish You that ;) ), You probably get impatient each time You request admin/orders.php page.

Good news is that there is quick fix. Just add index on (orders_id, class) on orders_total table to speed up the query. Here’s MySQL statement which does that for You:

ALTER TABLE orders_total ADD INDEX idx_oid_class(orders_id, class)

The query is still suboptimal, but at least its quite fast until orders table gets really big (500 000 orders? It’ll depend on Your server).

, , , ,

Store Credit is great module, unfortunately it’s preety inefecient in some places. For example, on every page request of admin/store_credit.php whole customers table is read, and for each customer additional SELECT and UPDATE query is executed to calculate and update pending points, even if the customer does not have any pending points!

It’s not a big deal when You have up to 1000 customers, but when there are more then 100 000 customers page loads in 30 seconds (store_credit.php executes over 2 x customer count,  or in this example over 200 000 queries on each request).

To address that, change following code in admin/store_credit.php:

function store_pending_rewards() {
global $db;

$customers = $db->Execute("SELECT customers_id FROM " . TABLE_CUSTOMERS . " ORDER BY customers_id ASC");
while (!$customers->EOF) {
$customers_id = $customers->fields[‘customers_id’];
$pending_rewards = $this->get_pending_rewards($customers_id);
$db->Execute("UPDATE " . TABLE_STORE_CREDIT . "
SET pending = "
. $pending_rewards . "
WHERE customers_id = "
. $customers_id . "
LIMIT 1"
);
$customers->MoveNext();
}
}

to:

function store_pending_rewards() {
global $db;

$customers = $db->Execute("SELECT DISTINCT customers_id FROM " . TABLE_SC_REWARD_POINT_LOGS . " ORDER BY customers_id ASC");
while (!$customers->EOF) {
$customers_id = $customers->fields[‘customers_id’];
$pending_rewards = $this->get_pending_rewards($customers_id);
if($pending_rewards == 0.0) { $customers->MoveNext(); continue; }
$db->Execute("UPDATE " . TABLE_STORE_CREDIT . "
SET pending = "
. $pending_rewards . "
WHERE customers_id = "
. $customers_id . "
LIMIT 1"
);
$customers->MoveNext();
}
}

Thanks to that, only customers who actually have some pending points will be processed.

, ,

Version v1.5.1 of Query Log is available. You can download it from: www.data-diggers.com/contribs/query-log/downloads/querylog-current.zip .

What’s new in this version:

  • You can now close ‘calculator’ layer (it’s the tool that highlights queries that match given regular expression)
  • You can now disable query logging completely. In previous versions Query Log kept all queries in memory, no matter if You wanted to log them (display them) or not.

, ,

I noticed that following query takes long time to execute:

SELECT DISTINCT op.products_id
FROM orders o, orders_products op, products p
WHERE o.customers_id = ‘2345′
AND o.orders_id = op.orders_id
AND op.products_id = p.products_id
AND p.products_status = ‘1′
GROUP BY products_id
ORDER BY o.date_purchased DESC
LIMIT 6

I noticed that there’s no index on orders table on customers_id field. Without it MySQL has to scan whole table to find orders by customers_id (in this case ‘2345′). If orders table is small there’s no problem, but when it has 10 000 or more entries it can noticably slow down Zen Cart. So, let’s add index on that table

ALTER TABLE `orders` ADD INDEX `idx_cid_datepurchased`(`customers_id`, `date_purchased`);

I included date_purchased in index, because some pages in admin area will use it for searching all orders of given customer.

In my case it decreased query time from 0.3 sec to 0.08 sec. It’s still too much, but it’s better then nothing.

, , , ,

Older posts >>

Find it!

Theme Design by devolux.org