Pxprpojection Data Projection What Is It

 

Hello everybody.

Imagine that you need to have implementation of several tables with possibility to update them all. 

Also you want to implement by the Acumatica side rather than by the database.

  • You know that Joined tables in Acumatica are read-only and you cannot update fields there
  • Can we create/use SQL-like views in Acumatica?
  • How can I join grouped (statistical) view to the DAC?

So, yes we can do it. For this purpose Acumatica gives you PXprojection attribute.

PXProjection attribute  binds the DAC to an arbitrary data set. The attribute thus defines a named view, but is implemented by the server side rather than by the database.

Lets start)

Steps:

  • Think whats tables you need to implement and whats of fields from this table;
  • Create new DAC that will represent view. This DAC may have less columns than you have in DACs you will select later. So just define fields you need. In the end it will help a bit with select performance;
  • Create new graph;
  • Create page - view of implementatoin;

For examle, I want to see implementatioin of 2 tables(CRactivity and BAccount) and fields like baccountID, baccountName from BAccount table and contactID, subject from CRactivity table.

In my solution I create new DAC class and define PXProjectionAttribute on this DAC.

 

[Serializable]
    [PXProjection(typeof(
        Select2<BAccount,
            InnerJoin<CRActivity,
                On<CRActivity.bAccountIDEqual<BAccount.bAccountID>>>>),
       Persistent = true
        )]
    public partial class PXprojectionJoinDacClass : IBqlTable
    {
        public abstract class baccountID : PX.Data.IBqlField
        {
        }
        // The field mapped to the BAccount field (through setting of BqlField)
        [PXDBInt(IsKey = true, BqlField = typeof(BAccount.bAccountID))]
        [PXExtraKey]
        [PXUIField(DisplayName = "BAccount ID")]
        public virtual int? BAccountID { getset; }
 
        public abstract class contactID : PX.Data.IBqlField
        {
        }
        // The field mapped to the CRactivity field
        // (through setting of BqlField)
        [PXDBInt(IsKey = true, BqlField = typeof(CRActivity.contactID))]
        [PXUIField(DisplayName = "Contact ID")]
        public virtual int? ContactID { getset; }
 
        public abstract class subject : PX.Data.IBqlField
        {
        }
        // The field mapped to the CRactivity field
        // (through setting of BqlField)
        [PXDBString(IsKey = false, BqlField = typeof(CRActivity.subject))]
        [PXUIField(DisplayName = "Subject")]
        public virtual string Subject { getset; }
 
        public abstract class acctName : PX.Data.IBqlField
        {
        }
        // The field mapped to the Baccount field
        // (through setting of BqlField)
        [PXDBString(IsKey = false, BqlField = typeof(BAccount.acctName))]
        [PXUIField(DisplayName = "Account Name")]
        public virtual string AcctName { getset; }
 
    }

As you see I use Select2 attribute for select from 2 tables and use BQL command. In my way it is InnerJoin.

For PXProjection constructor you should provide BQL command that will define what tables you want to select. You may use all possible commands of BQL (Join, Where, GroupBy, OrderBy).

Also I define "Persistent = true" for update both tables, without it my implementatiioin will be only for read. 

Go next and create new graph.

 

public class ProjectionMaint : PXGraph<ProjectionMaintPXprojectionJoinDacClass>
   {
       public PXSelect<PXprojectionJoinDacClass> Projections;
   }

Easy, we only use PXSelect from PXprojectionJoinDacClass.

Go next and create new page.

 

<%@ Page Language="C#" MasterPageFile="~/MasterPages/FormDetail.master" 
AutoEventWireup="true" ValidateRequest="false"  CodeFile="PR101000.aspx.cs" Inherits="Page_PR101000" Title="Untitled Page" %> <%@ MasterType VirtualPath="~/MasterPages/FormDetail.master" %>   <asp:Content ID="Content1" ContentPlaceHolderID="phDS" runat="Server">     <px:PXDataSource ID="ds" runat="server" Visible="True" SuspendUnloading="False"                       TypeName="ClassLibrary1.ProjectionMaint" PrimaryView="Projections">     </px:PXDataSource> </asp:Content> <asp:Content ID="cont2" ContentPlaceHolderID="phG" runat="Server">     <px:PXGrid ID="grid" runat="server" Height="400px" Width="100%" Style="z-index100"                AllowPaging="True" AllowSearch="True" AdjustPageSize="Auto"
 DataSourceID="ds" SkinID="Primary"                 TabIndex="800" TemporaryFilterCaption="Filter Applied">         <Levels>             <px:PXGridLevel DataKeyNames="BAccountID" DataMember="Projections">                 <Columns>                     <px:PXGridColumn DataField="BAccountID" Width="90px">                         <ValueItems MultiSelect="False">                         </ValueItems>                     </px:PXGridColumn>                     <px:PXGridColumn DataField="ContactID" Width="200px">                         <ValueItems MultiSelect="False">                         </ValueItems>                     </px:PXGridColumn>                     <px:PXGridColumn DataField="Subject" Width="200px">                         <ValueItems MultiSelect="False">                         </ValueItems>                     </px:PXGridColumn>                     <px:PXGridColumn DataField="AcctName" Width="200px">                         <ValueItems MultiSelect="False">                         </ValueItems>                     </px:PXGridColumn>                 </Columns>             </px:PXGridLevel>         </Levels>         <AutoSize Container="Window" Enabled="True" MinHeight="200" />     </px:PXGrid> </asp:Content>

So here we use only PXGrid where we define fields.

After add this page to site map and Go to page PR101000 to test.

All work fine. Make sure that fields updateble in both tables.

Also do not forget that you can use 2 and more tables in one implementation. 

Thank you for reading. Have you question? Please leave comments here.

No Comments

Add a Comment
 

 

How To Increase Display Size Of Items In Sql Server Management Studio

 

Hello everybody,

today I want to document one interesting feature of SQL Server management studio. Some time it happens, that you work with big XML data files.

For example if you have sql statement like this:

SELECT @xm=(
select rowid, count(rowid) as IdsNumber from SOLine s where s.rowid like 'fdsafewvvcxkfdsla'fslf%'
group by rowid
for xml raw, root)
select @xm

in SQL server management studio and would like to increase size that is workable by SQL server management studio. I found that following value in system registry is very helpful:

HKCU\Software\Microsoft\SQL Server Management Studio\14.0_Config\XmlEditor\MaxFileSizeSupportedByLanguageService

set it's value to 100 and you'll have option to open 100 M xml files

No Comments

Add a Comment
 

 

How To Separate Automation Schedules In Acumatica

 

Hello everybody,

take a loot at the following picture:

Let's say that you would like to have two Acumatica instances connected to the same database. Is it possible? Definetely yes, just with pointing both of them to the same connection string and you'll get some kind of scalability. 

But imagine that your Acumatica has execution of some automation schedules. How to make sure, that only one of them will be executor of Automation schedules, not both of them?

Very simple. Just add this key to web.config of Acumatica which should not be Automation schedules executor:

 <add key="DisableScheduleProcessor" value="True"/>

Default value of DisableScheduleProcessor key is false, so you need to say which Acumatca instance shouldn't think about execution schedules. With this simple trick you can have two or even more instances of Acumatica and regulate which of those instances will execute schedules

No Comments

Add a Comment
 

 

How To Modify Stock Item Screen In202500 In Acumatica

 

Hello everybody,

today I want to describe how to extend Stock Item screen IN202500 in Acumatica. Imagine that you need to add to tab General settings two selectors. Suppose that you need to have two selectors:

as you can see following need to be achieved:

  1. To tab General Settings it is needed to add selectors: "Clase articulo web" and "Subclase articulo web".
  2. In case if selector "Clase articulo web" changes, then "Subclase articulo web" should show some other values.

The first step should be start Acumatica developer project as described here.

For cases if we have dependency of one selector from another it is possible to program in two ways:

  1. Custom selector for dependent code.
  2. Describe dependency in DAC class or DAC class extension.

Option number 1 or custome selectors were already described at mine blog here

Let's take a look at second scenario. Before we continue let's create two tables: UsrArticul and UsrSubArticul. In order to make life simple, you can use SQL below in order to follow me:

SET ANSI_NULLS ON
 
 
SET QUOTED_IDENTIFIER ON
 
 
CREATE TABLE [dbo].[UsrArticul](
	[CompanyID] [INT] NOT NULL,
	[ArticulID] [INT] IDENTITY(1,1) NOT NULL,
	[ArticulCD] [NVARCHAR](50) NULL,
	[ArticulName] [NVARCHAR](50) NULL,
 CONSTRAINT [PK_Articul] PRIMARY KEY CLUSTERED 
(
	[CompanyID] ASC,
	[ArticulID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 
) 
 
SET ANSI_NULLS ON
GO
 
SET QUOTED_IDENTIFIER ON
GO
 
CREATE TABLE [dbo].[UsrSubArticuls](
	[CompanyID] [int] NOT NULL,
	[ArticulID] [int] NOT NULL,
	[SubArticulID] [int] IDENTITY(1,1) NOT NULL,
	[SubArticulCD] [nvarchar](50) NULL,
	[SubArticulName] [nvarchar](50) NULL,
 CONSTRAINT [PK_UsrSubArticuls] PRIMARY KEY CLUSTERED 
(
	[CompanyID] ASC,
	[ArticulID] ASC,
	[SubArticulID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
) 

I always use prefix Usr in Acumatica in order to notify updater of Acumatica that during upgrade of version those two tables shouldn't be deleted. 

Next let's insert some demo data:

INSERT INTO [dbo].[UsrArticul] ([CompanyID] ,[ArticulCD] ,[ArticulName]) VALUES (2, 'ART1', 'Articul 1' )
INSERT INTO [dbo].[UsrArticul] ([CompanyID],[ArticulCD],[ArticulName]) VALUES (2, 'ART2', 'Articul 2' )
INSERT INTO [dbo].[UsrArticul]([CompanyID],[ArticulCD],[ArticulName]) VALUES (2, 'ART3', 'Articul 3' )
INSERT INTO [dbo].[UsrSubArticuls] ([CompanyID] ,[ArticulID] ,[SubArticulCD] ,[SubArticulName]) VALUES (,,'SUB1A' ,'SUB 1 A')
INSERT INTO [dbo].[UsrSubArticuls] ([CompanyID] ,[ArticulID] ,[SubArticulCD] ,[SubArticulName]) VALUES (,1, 'SUB1b' ,'SUB 1 b')
INSERT INTO [dbo].[UsrSubArticuls] ([CompanyID] ,[ArticulID] ,[SubArticulCD] ,[SubArticulName]) VALUES (,1, 'SUB1c' ,'SUB 1 c')
INSERT INTO [dbo].[UsrSubArticuls] ([CompanyID] ,[ArticulID] ,[SubArticulCD] ,[SubArticulName]) VALUES (,1,  'SUB1d','SUB 1 d')
INSERT INTO [dbo].[UsrSubArticuls] ([CompanyID] ,[ArticulID] ,[SubArticulCD] ,[SubArticulName]) VALUES (2, 2,'SUB2A','SUB 2 A')
INSERT INTO [dbo].[UsrSubArticuls] ([CompanyID] ,[ArticulID] ,[SubArticulCD] ,[SubArticulName]) VALUES (2, 2, 'SUB2b' ,'SUB 2 b')
INSERT INTO [dbo].[UsrSubArticuls] ([CompanyID] ,[ArticulID] ,[SubArticulCD] ,[SubArticulName]) VALUES (2, 2, 'SUB2c' ,'SUB 2 c')
INSERT INTO [dbo].[UsrSubArticuls] ([CompanyID] ,[ArticulID] ,[SubArticulCD] ,[SubArticulName]) VALUES (2, 2, 'SUB2d' ,'SUB 2 d')
INSERT INTO [dbo].[UsrSubArticuls] ([CompanyID] ,[ArticulID] ,[SubArticulCD] ,[SubArticulName]) VALUES (2, 3, 'SUB3A', 'SUB 3 A')
INSERT INTO [dbo].[UsrSubArticuls] ([CompanyID] ,[ArticulID] ,[SubArticulCD] ,[SubArticulName]) VALUES (2, 3, 'SUB3b', 'SUB 3 b')
INSERT INTO [dbo].[UsrSubArticuls] ([CompanyID] ,[ArticulID] ,[SubArticulCD] ,[SubArticulName]) VALUES (2, 3, 'SUB3c', 'SUB 3 c')
INSERT INTO [dbo].[UsrSubArticuls] ([CompanyID] ,[ArticulID] ,[SubArticulCD] ,[SubArticulName]) VALUES (2, 3,'SUB3d', 'SUB 3d')

We have three root articuls and each of those root articuls has 4 child sub articuls. And task is the following, if User select Articul 1 at top selector, then bottom selector should show SUB 1 A, SUB 1 b, Sub 1 c and Sub 1 d. 

In order to have access to those two controls on page IN202500 we need:

  1. Create DAC classes for tables UsrArticule and UsrSubArticuls
  2. Exted DAC class InventoryItem with declaration of two fields as selectors
  3. Add two selectors on the page

In order to create two DAC classes, you can use either standard Acumatica DAC class generator, or download mine utility which does the same. If you decide to use utility then also keep in mind that you need delete from generated code following fields: CreatedByID, CreatedByScreenID, CreatedDatetime, LastModifiedByID, LastModifiedByScreenID, LastModifiedDateTime, Tstamp. I skipped those fields that Acumatica uses in order to make sample easier to understand. But in real life projects I definetely recommend to have those fields especially if you have multiuser environment where few users can modify the same entity. In that case those service fields is a must.

Take a look at two DAC classes about UsrArticul and UsrSubArticuls:

[Serializable]
    public class UsrArticul : IBqlTable 
    {
        #region ArticulID
        public abstract class articulID : IBqlField
        {
        }
        [PXDBInt(IsKey = true)]
        [PXUIField(DisplayName = "Articul ID", Visibility = PXUIVisibility.Visible, Visible = false, Enabled = false)]
        public virtual int? ArticulID { getset; }
 
        #endregion
 
        #region route
        public abstract class articulCD : IBqlField
        {
        }
        [PXDBString(50)]
        [PXUIField(DisplayName = "Articul CD", Visibility = PXUIVisibility.Visible)]
        public virtual string ArticulCD { getset; }
        #endregion
 
        #region route
        public abstract class articulName : IBqlField
        {
        }
        [PXDBString(50)]
        [PXUIField(DisplayName = "Articul Name", Visibility = PXUIVisibility.Visible)]
        public virtual string ArticulName { getset; }
        #endregion
       
    }

and another class:
    [Serializable]
    public class UsrSubArticuls : IBqlTable 
    {
        #region ArticulID
        public abstract class articulID : IBqlField
        {
        }
 
        [PXDBInt()]
        [PXUIField(DisplayName = "Articul ID", Visibility = PXUIVisibility.Visible, Visible = false, Enabled = false)]
        public virtual int? ArticulID { getset; }
 
        #endregion
 
        #region SubArticulID
        public abstract class subArticulID : IBqlField
        {
        }
 
        [PXDBInt(IsKey = true)]
        [PXUIField(DisplayName = "Sub Articul ID", Visibility = PXUIVisibility.Visible, Visible = false, Enabled = false)]
        public virtual int? SubArticulID { getset; }
 
        #endregion
 
        #region route
        public abstract class subArticulCD : IBqlField
        {
        }
        [PXDBString(50)]
        [PXUIField(DisplayName = "Sub Articul CD", Visibility = PXUIVisibility.Visible)]
        public virtual string SubArticulCD { getset; }
        #endregion
 
        #region route
        public abstract class subArticulName : IBqlField
        {
        }
        [PXDBString(50)]
        [PXUIField(DisplayName = "Sub Articul Name", Visibility = PXUIVisibility.Visible)]
        public virtual string SubArticulName { getset; }
        #endregion
        	
    }

 Next step - create extension class, what are you use. In this example it InventoryItem DAC class:

public class InventoryItemExt : PXCacheExtension<InventoryItem>
   {
       public abstract class usrArticul : IBqlField
       {
       }
 
       [PXDBInt()]
       [PXDefault(PersistingCheck = PXPersistingCheck.Nothing)]
       [PXUIField(DisplayName = "Articul CD")]
       [PXSelector(typeof(Search<UsrArticul.articulID>), SubstituteKey = typeof(UsrArticul.articulCD))]
       public virtual int? UsrArticul { getset; }
 
       public abstract class usrSubArticul : IBqlField
       {
       }
 
       [PXDBInt()]
       [PXSelector(typeof(Search<UsrSubArticuls.subArticulIDWhere<UsrSubArticuls.articulIDEqual<Current<InventoryItemExt.usrArticul>>>>), 
           SubstituteKey = typeof(UsrSubArticuls.subArticulCD))]
       [PXDefault(PersistingCheck = PXPersistingCheck.Nothing)]
       [PXUIField(DisplayName = "Sub Articul CD")]
       public virtual int? UsrSubArticul { getset; }
 
   }

Here I describe two selectors. Overhead and downhead. 

Second selector depend of first. Also pay attention that I used  Equal<Current<InventoryItemExt.usrArticul> (not UsrArticul.articulID), for correct filtering because in this moment when you select first field it is "Current "selector, and you use "YourDacExt.Field".

or in full picture:

[PXSelector(typeof(Search<UsrSubArticuls.subArticulID, Where<UsrSubArticuls.articulID, Equal<Current<InventoryItemExt.usrArticul>>>>), 
           SubstituteKey = typeof(UsrSubArticuls.subArticulCD))]

Staff in bold will allow you to select only sub group.

Do not forget build your project!

After that add controls(PXSelector) to .aspx  view page:

<px:PXSelector CommitChanges="True" ID="usrArticul1" runat="server" DataField="UsrArticul" AllowEdit="True" ></px:PXSelector>
<px:PXSelector CommitChanges="True" ID="usrSubArticul1" runat="server" DataField="UsrSubArticul" AutoRefresh="True" AllowEdit="True" ></px:PXSelector>

After that open this page, and you can see this controls:

 Verify second selector:

No Comments

Add a Comment
 

 

Clock

 

Hello everybody,

today I want to leave a note about one of mine customers. And you know, as soon as I've started to write this post I've caught myself that I can't name one person for whom I provide services for money with word customer. Please continue reading and you'll grasp what I mean. 

Have you ever wonder how it could happen that Steve Jobs, guy that is not a designer, not UI-UX designer could orchestrate development of such cool devices? I always dreamed to meet such a person in life, not just read about them in wikipedia, or to read their books. And I can say that recently I've meet such a person. 

Everything started from project that he asked me to create. And of course, I'm always or almost always ready to work with somebody, who has burning eyes and believes in his idea. I mean I not always work with somebody who just has money but don't believe or don't like his own business idea. Why not? Because plenty of business ideas are tempted to crash. That is reality. Plenty of great ideas were crashed because some of them were to early on market. Some of them crashed because they were to late on the market. Or to early. 

So I've meet with that guy, and we started our cooperation. When project become closer to it's completion mine customer asked me questions, that for majority of programmers mind can be hardly to grasp. For example: does this web site has feeling that it's like new, just finished building. Like you see that building is ready, doors are looking good, windows are looking nice, but some elements give you feeling that it just was finished recently. Have you ever faced such a question as a developer? Me personally not. That customer continued. Take a look at mine watch. I bought it because I like how it look like. How lines are made, how clear time is showing up. It doesn't have any fancy night illumination or any other stylish staff. I took a look. And to be honest I couldn't share his enjoyment of this clock. Then customer continued and said, how do you think, how much does it cost? I expressed guess like 80$. I was wrong. That watch costs 2000$!!! 

Next few evenings I've decided to spent on watches shops in order to find the difference between different clocks. I should admit, I still far away from Steve Jobs. 

No Comments

 

Add a Comment
 

 

Operator In In Bql

 

Hello everybody,

today I want to write a few words about operator in which was presented in SQL for long ago, but weren't available in Acumatica BQL. But time goes on and now you can use it. For example like this:

Object[] values = new String[] { "BXW000004""BXW000005" };
 
                POOrder item = PXSelect<POOrder,
                    Where<POOrder.orderNbrIn<Required<POOrder.orderNbr>>>>.Select(Base, values);

that code will generate following sql statement:

Select * from POOrder POOrder Where POOrder.OrderNbr In ('BXW000005', 'BXW000004')
	Order by POOrder.OrderNbr

I can say that such approach simplifies some tasks that require dynamic passing of arguments.

No Comments

Add a Comment
 

 

Functions Map Zip And Lambda In Python

 

Hello everybody,

today I want to describe three elements of Python: map, zip, lambda and *.

Zip and *

The first step that I want to describe is zip and * usage. Take a look at the following code:

a = [5, 6]
b = [7, 8]
 
c = zip(a,b)
print(*c)

How do you think, what will be output, if I'll tell you that zip function zips arrays? If your guess is (5, 6) (7, 8) then unfortunately you are wrong. Output will be the following:

(5, 7) (6, 8). I suppose that zip name was chosen because as usually zippers on clothes as usually vertical. Zip functions "zips" elements by columns, like presented on the picture:

Now one more question, what is purpose of * ? It tells to Python interpreter to treat each element and not just reference to memory.

Function map

Next function that I'd like to present goes map. That function is needed for cases if you need to apply the same function for element of array and get another array after that function. For such purposes exists function map. Take a look at the code:

def squareFun(x):
    return x*x
 
numbers = [5, 6, 8]
for number in numbers:
    print( squareFun(number))

as result you'll get squares of numbers. In Python you can do it in another way with usage of list comprehensions:

def squareFun(x):
    return x*x
 
numbers = [5, 6, 8]
print( [squareFun(numfor num in numbers])

which is fine and good way to go. 

And now take a look how to achieve the same with map:

def squareFun(x):
    return x*x
 
numbers = [5, 6, 8]
print(* map(squareFun, numbers))

I can't say that in this particular example it's easier or nicer, but you've got an idea.

If you incline better to see standard description, then take a look how map is declared. For example like this:

r = map(func, seq)

As you can see map takes two parameters: some function func and some sequence of elements seq. map() will apply function func to each element of the sequence seq and will return sequence on which function was applied. 

Next try to compare it with 

map + lambda

Take a look at the following code sample:

numbers = [5, 6, 8]
print( *map(lambda xx*x, numbers))

take note that we don't have function squareFun described. You may wonder, why somebody can have desire to use lambda isntead of full grown function? The main reason is that lambda function can be written in one line, while function with formatting will require 4 lines with formatting. It's like ++ operator in C#, Java or C++. It's easier to write i++ then i = i + 1. One more point to keep in mind is that lamda expressions can be used when it is needed to pass in other function some kind of calculations.

No Comments

Add a Comment
 

 

Scaling Acumatica Horizontally

 

Hello everybody,

today I want to share one interesting piece of information about horizontal scaling of Acumatica. I mean as usually for majority of people it is clear that Acumatica can work on one machine when DB, IIS and Acumatica lives on one machine. 

One more variant of scaling can be when you have IIS with Acumatica on one machine, and DB on another machine. It can look like this:

Here User lives on his machine, Acumatica ERP server lives on second machine, and site database lives on third machine. That is pretty good working schema especially for cases if you need vertical scalability.

But recently I've discovered little bit more horizontal scalability for Acumatica which looks like this:

And take note, that with such a schema user request will pass through load balancer. You can use as way of example nginx. And one more important feature that really speeds up your performance will be Redis. That scalability option will have following features:

  1. If data is not persisted to db, then it will be stored at Redis. 
  2. You can have as much as you'd like ( or can afford ) to have Acumatica ERP server

Some additional comments on such a schema. First of all I'd like to mention that nginx is quite efficient load balancer, so you can really use it for making your life easier. There are some options but for now I'm inclined to recommend to use nginx. One of the reasons of such a sticking is plenty of documentation over it.

For session storage currently I'm more sticked to Redis. And main reason for such an approach is that Redis is also scalable. While MS SQL is also scalable, but as mathematicians say oranges should go with oranges, apples with apples. Redis was born with scalability in mind, while SQL was born for efficient persisting your data. 

As green lines on the schema shows, some time it can happen that your user persisted some data on Node1, made some manipulations and switched to Node2 and that will be done seemlessly. 

No Comments

Add a Comment
 

 

How To Merge Precision And Recall

 

Hello everybody,

today I continue writing notes about measuring quality of learning. You can read my previuos artcile 1 and article 2 about measures of quality of learning. If to summarize two of those articles we have the following:

  1. accuracy is good measure, but if samplings is unbalanced then accuracy can have great numbers, but total model will be very bad.
  2. precision tells you how objects model a(x) can find
  3. precision and recall work fine on unbalanced samplings

And then question arises, is it possible somehow to merge them, but not as accuracy but as something more meaningful then accuracy?

I'll decribe different ways to describe it going from worsest to better and hopefully the best algorithm.

Average

Let's consider first example: average value of precision and recall. Take a look at the following formula:

Take a look how possible chart can look like:

First of all you can see lines drawed with step 0.2 . That is because formula of precision is linear formula. 

consider following case. You've made a model that gives you following criteria

precision = 0.1

recall = 1

A = 0.5 * (1 + 0.1) = 0.55

In reality that can be constant output model, that always says 1 as response, and if you have biased selection set which is 99% with 1 and only 1% with -1. That model is useless, but on chart it will look like this:

Consider another model:

precision 0.55

recall 0.55

A = 0.55

Combined picture will look like this:

second model is much better then the first one, but both dots live on the same line giving idea that both of them are equally good. But in reality they are not. The second one is much better. Constant and good enough alrorithm receive the same measurement of quality. 

Minimum value

Ok, so let's throw away average value, and instead try optimization minimum value. Like in this forumla:

M = min(precision, recall)

Then just empty chart will look like this:

as you can see on the picture, good algorithms will be concentrated in top right corner. And the worse algorithms will be closer to left bottom part. 

But still it doesn't perform as well as we would like to. Consider two algorithms:

1.1.  precision 0.4

1.2. recall 0.5

1.3. M = 0.4

second one:

2.1. precision = 0.4

2.2. recall = 0.9

2.3. M = 0.4

They will look on chart like this:

so, it is clear that second alorithm is better because with the same precision it gives higher recall. But on chart it is located at lover place, closer to bottom. Also both algorithms lie on the same linve, while in reality it shoudn't be the case. 

F-Measure

One more idea can be to smooth those lines with help of garmonic average, which is also named F-Measure. It has following formula:

Quite often β is assigned value 1. In that case F-measure look like this:

β plays a role of weight for precision ( in other words importance of precision ).

Consider two algorithms:

first:

  1. precision = 0.4
  2. recall = 0.5
  3. F = 0.44

second:

  1. precision = 0.4
  2. recall = 0.9
  3. F = 0.55

and how they look at chart:

so you can see that second algorithm is closer to right top corner or closer to ideal classifier. 

Let's come back to this formula:

Imagine following scenario. If you want to give priority to precision, what should be value of β ? Something bigger then 1. For example 2. 

If you want to give priority to recall, then β should be smaller then 1. For example 0.5. 

No Comments

Add a Comment
 

 

Automation Shedule Screen Is Not Executed

 

Hello everybody,

today I want to describe interesting feature of Acumatica related to back ups of database. Imagine the following: you've restored database from back up at your dev environment. And let's say you have automation schedule that every hour you should send to each contact some emails. So, you've restored customization and would like to find the button that will block sending of all emails. At which screen it is? At none. It is interesting to know, that Acumatica already "pressed" at this button when you've restored db from back up. But for now you probably have another question. How to debug execute any schedule at all?

I propose following steps:

  1. Delete all automation schedules.
  2. Create new automation schedule just fory our screen.

Delete all automation schedules

That is relatively simple task. As one of the ways you can execute following sql:

UPDATE AUSchedule set DeletedDatabaseRecord = 1

After that your screen SM205030 ( aka Automation Schedules ) will look pretty lonely and orphaned, but your partners will not mark your mail server as spam after you initialize schedule

Create new schedule

  1. Navigate to screen SM205020
  2. Create schedule with usage of screen that you want to use

Come back to screen SM205030 and press at button Initialize Scheduler as presented at screenshot:

and after that you'll have opportunity to debug your screen. Take note that button Initialize scheduler can't be unpressed. 

No Comments

Add a Comment