API Examples

Complete and diverse set of AI use-cases written in Javascript. Each example covers data integration, transformation, reasoning/learning, runtime configuration and results handling.

NBA Super Star

NBA expert decision making on who was an NBA super star

Customer Churn

Identify which customers are going to leave you

The Stock is on Fire!

Spot a when a stock starts trending

 

Perfect Ski Vacation

Recommend personalized ski offerings at real-time

Twitter Optimization

Find the perfect time to tweet when my followership is active

Drug Designer Search

Find protein functional sites in an efficient manner

Predict Customer Churn

You are the customer relations manager of a company that offers a subscription music streaming service. You want to be able to predict which customers won’t renew their subscription during the 30 days after their subscription expires to incentivize them and prevent churn. You know that the behavior patterns of customers over time are significant for determining if a customer may churn and seek a solution that can accurately capture such patterns.

Gameplan

Task: Predict which of the currently subscribed customers are likely to churn based on historical data of both types of customers: those that eventually churned and those that didn’t.

Search for Patterns where:

  1. Only the most recent 14 day activity of a specific user is considered.
  2. Churn behavior is learned from users who had a subscription ending in Feb. 2017 which was either renewed more than 30 days after the subscription ended or not renewed at all.
  3. Non-churn behavior is learned from users who had a subscription ending in Feb. 2017 which was renewed within 30 days.
  4. The patterns are discriminative, i.e. significantly more common among one type of users than the other.
Source Code
const bcToken = "my_braincast_token";
const bcSecretKey = "my_secret_api_key";
const bc = BC.createClient(bcToken, bcSecretKey);

const ChurnAdapterTemplate = {
    openAPISpec: Examples.OpenAPI.JSON.Churn, // the Open API Specification (OAS) of a REST data source for accessing the churn user data
    path:"/query",  // the HTTP endpoint to use from the OAS spec
    operation:"get",  // the HTTP method to use
    params:{     // required values for parameters specified in the OAS
        "apikey":     "AKIAIOSFODNNSLOE:Yxg83MZaEgh3OZ3l0rLo5RTX11o="
    }
};

//
// define and validate data sources
//
let learningStream = bc.Datastore.getStream("Churn.Example.UserTransactionAndActivity");
if (!learningStream) {//Create a stream of user transactions and listening activity between 1-10-2016 and 31-3-2017 to learn from:
    learningStream = bc.Datastore.defineStream(
        {
            "source": "Churn.Example.Learning",//source name
            "adapter": BC.Utils.SourceAdapters.createREST(ChurnAdapterTemplate,{"params": {"function": "user_transactions_and_listening_activity"}}),
            "record": { // provide an object to specify which attributes of the records being fetched from the source should be used (if omitted, all attributes will be used) - each property is an attribute and the value is that attribute's description:
                "date": "the day at which the record's data occurs",
                "userId": "the user's identifier",
                "totalHours": "the number of total listening hours rounded to the nearest integer",
                "totalSongs": "the total number of songs listened to",
                "isCancel": "true if there was a cancellation transaction on this day, otherwise false",
                "isAutoRenew": "true if there was an automatic subscription renewal transaction on this day, otherwise false",
                "paymentPlanDays": "number of days in the current subscription period",
                "currentSubscriptionEnd": "the day that the subscription on this day ends",
                "nextSubscriptionDate": "the day that the user renewed the subscription after the current subscription ended"
            },
            "filter": {"where": "date >= 20161001 AND date <= 20170331"}, // the filter for this source specifying that only the records between 1-10-2016 and 31-3-2017 should be consumed
        }
    );
    if (!learningStream || !learningStream.validate()) {
        throw "Example learning stream cannot be accessed, reason: " + learningStream.validationErrorMsg;
    }
}

let predictionStream = bc.Datastore.getStream("Churn.Example.UserTransactionAndActivity");
if (!predictionStream) {//Create a stream of user transactions and listening activity between 1-10-2016 and 31-3-2017 to learn from:
    predictionStream = bc.Datastore.defineStream(
        {
            "source": "Churn.Example.Learning",//source name
            "adapter": BC.Utils.SourceAdapters.createREST(ChurnAdapterTemplate,{"params": {"function": "user_transactions_and_listening_activity"}}),
            "record": { // provide an object to specify which attributes of the records being fetched from the source should be used (if omitted, all attributes will be used) - each property is an attribute and the value is that attribute's description:
                "date": "the day at which the record's data occurs",
                "userId": "the user's identifier",
                "totalHours": "the number of total listening hours rounded to the nearest integer",
                "totalSongs": "the total number of songs listened to",
                "isCancel": "true if there was a cancellation transaction on this day, otherwise false",
                "isAutoRenew": "true if there was an automatic subscription renewal transaction on this day, otherwise false",
                "paymentPlanDays": "number of days in the current subscription period",
                "currentSubscriptionEnd": "the day that the subscription on this day ends",
                "nextSubscriptionDate": "the day that the user renewed the subscription after the current subscription ended"
            },
            "filter": {"where": "DATE(date,'YYYYMMDD') >= CURDATE(-3)"},  // the filter for this source specifying that only records for the past 3 months should be consumed
            "rate": BC.Meta.Period.Week,  // the input consumption rate specifying that all available data records should be consumed once every week
        }
    );
    if (!predictionStream || !predictionStream.validate()) {
        throw "Example prediction stream cannot be accessed, reason: " + predictionStream.validationErrorMsg;
    }
}

// Specify constraints that define which data instances in both learning and prediction streams should be analyzed (i.e. to learn from and make predictions about)
// IMPORTANT NOTE: these constraints must be defined over attributes that are common to the records of both learning and prediction streams!
let instanceConstraints = [
    BC.Meta.Functions.Constraints.newConstraint(
        "user14DaySequence", // constraint name
        function (curSequence) {
            return  curSequence.records.length === 14 &&
                    curSequence.records.every(rec=>rec["userId"]===curSequence.records[0]["userId"]);
        },
        "instance is a specific user's 14 day long sequence", // explanation
    ),
    BC.Meta.Functions.Constraints.newMaximalValueConstraint(
        "mostRecentSequence",
        function (curSequence) { //  evaluator function - a function whose returned value will be used to select one of all possible sequences satisfying all other constraints
            let avgDate = 0;
            curSequence.records.forEach(rec=>avgDate+=rec["date"]);
            return  avgDate / curSequence.records.length;
        },
        "instance is the most recent sequence (with the latest average date of all records)"
    )
];

// Specify constraints that check whether or not an instance in the learning stream represents a certain type:
let typeConstraints = [
    BC.Meta.Functions.Constraints.newConstraint(
        "churn", // constraint name
        function (curSequence) {
            let lastDayRec = curSequence.records[curSequence.records.length-1];
            return  lastDayRec["currentSubscriptionEnd"] >= 20170201 && lastDayRec["currentSubscriptionEnd"] <= 20170301 && // the last day is within a user's subscription period which ends in Feb. 2017
                (!lastDayRec["nextSubscriptionDate"] || // and this user did not have another subscription period after Feb. 2017
                    numOfDaysBetween(lastDayRec["currentSubscriptionEnd"],lastDayRec["nextSubscriptionDate"] > 30)) // or this user had another subscription period but it began more than 30 days after the previous subscription period ended
        },
        "churn users who had a subscription ending in Feb. 2017 which the user either renewed more than 30 days after the subscription ended or didn't renew at all" // explanation
    ),
    BC.Meta.Functions.Constraints.newConstraint(
        "not churn", // constraint name
        function (curSequence) {
            let lastDayRec = curSequence.records[curSequence.records.length-1];
            return  lastDayRec["currentSubscriptionEnd"] >= 20170201 && lastDayRec["currentSubscriptionEnd"] <= 20170301 && // the last day is within a user's subscription period which ends in Feb. 2017
                (!lastDayRec["nextSubscriptionDate"] || // and this user did not have another subscription period after Feb. 2017
                    numOfDaysBetween(lastDayRec["currentSubscriptionEnd"],lastDayRec["nextSubscriptionDate"] > 30)) // or this user had another subscription period but it began more than 30 days after the previous subscription period ended
        },
        "An instance in the learning stream is of type 'not churn' if it corresponds to a user who had a subscription ending in Feb. 2017 which the user renewed within 30 days" // explanation
    )
];

// Build a prediction scanner that can scan the behaviour of the currently subscribed users in the prediction stream and predict which users are likely to churn:
let predictionScanner = bc.Datastore.getScanner("Churn.Example.churnPredictor");
if (!predictionScanner) {
    predictionScanner = bc.Datastore.createPredictionScanner(
        learningStream, // the stream from which the scanner will learn to how to predict an instance's type
        predictionStream, // the stream containing the instances whose type needs to be predicted.
        instanceConstraints, // constraints that define what a data instance is in both learning and prediction streams, i.e. must be defined over attributes that are common to the records of both learning and prediction streams
        typeConstraints // constraints that define the different types of data instances (an instance may be of one, many or none of these types)
    );
    if (!predictionScanner.validate()) {
        throw "Scanner not configured properly, reason: " + predictionScanner.validationErrorMsg;
    }

    predictionScanner.settings = {
        "schedule": BC.Meta.Period.Week, // or BC.Meta.Period.Once which is the default
        "predict": "churn", // set the type of user behaviour to predict by specifying one of the type constraint's name
        "activation": BC.Utils.ActionAdapters.createJSCallback(function (result) {
            let predictedUserSequence = result.value;
            let probability = Number.parseFloat(result.probability) * 100;
            alert(`User: ${predictedUserSequence.records[0]["userId"]} is ${probability}% likely to churn because" + ${result.explanation}`);
        })
    };

    predictionScanner.save("Churn.Example.churnPredictor");
}

// start predicting churn users:
predictionScanner.predict();

Spot a Trending Stock at Real-time

You are the product manager of a successful stocks news mobile app, and you want to add a smart alerting service so your customers can be notified immediately when a specific stock of interest trends upwards. It will increase your customer retention dramatically. 

Gameplan

Task: Monitor the changes in a specific stock price and volume as well as the NASDAQ index price and volume and search for unique behavioral patterns within 10 seconds time frames.

Search for Patterns where:

  1. There is a sequence of at least two price changes in Apple’s stock that increase from one change to another​.
  2. The trend occurs within a 10-second window.
  3. At every second of the trend:
    1. Apple’s stock trading volume is higher than the average of the last minute.
    2. Apple’s stock price change is 50% higher than the last minute average.
    3. NASDAQ index price change is 50% higher than the last minute average.
Source Code
const bcToken = "my_braincast_token";
const bcSecretKey = "my_secret_api_key";
const bc = BC.createClient(bcToken, bcSecretKey);

const alphaVantageAdapterTemplate = {
    openAPISpec: Examples.OpenAPI.JSON.Alphavantage.Intraday, // the Open API Specification (OAS) for our REST data source
    path: "/query",  // the HTTP endpoint to use from the OAS spec
    operation: "get",  // the HTTP method to use
    params: {     // required values for parameters specified in the OAS
        "function": "TIME_SERIES_DAILY_ADJUSTED",
        "interval": "1sec", //Get 1 second ticks
        "apikey": "AKIAIOSFODNNSLOE:Yxg83MZaEgh3OZ3l0rLo5RTX11o="
    }
};

let pastDayFilter = {"where":"created_at > "+new Date().getTime()+" - 1000*60*60*24"};

//
// define and validate data source
//
let stream = bc.Datastore.getStream("Stocks.Example.AAPLandQQQ");
if (!stream) {
    stream = bc.Datastore.defineStream(
        {
            "source": "Stocks.Example.AAPLandQQQ",//source name
            "adapter": BC.Utils.SourceAdapters.createREST(alphaVantageAdapterTemplate, {"params": {"symbol": ["AAPL", "QQQ"]}}),// Use adapter to fetch data for Apple stock and NASDAQ index
            "record": { // provide an object to specify which attributes of the records being fetched from the source should be used (if omitted, all attributes will be used) - each property is an attribute and the value is that attribute's description:
                "timestamp": "the timestamp at which the record's data occurs",
                "stockCng": "the difference between the current and previous price of the stock",
                "stockAvgCng": "the average of all stock change values occurring in the last minute",
                "ratioStockVol": "the the current stock volume divided by the average stock volume in the last minute",
                "idxCng": "the difference between the current and previous price of the index",
                "idxAvgCng": "the average of all index change values occurring in the last minute"
            },
            "filter": pastDayFilter, // the filter for this source specifying that only the data for the past day should be consumed
            "rate": BC.Meta.Period.Min  // the input consumption rate specifying that all available data from the input source should be consumed once every minute - can be BC.Meta.Period.Event for continuous consumption of records as they are generated or BC.Meta.Period.Once which is the default.
        }
    );
    if (!stream || !stream.validate()) {
        throw "Example stream cannot be accessed, reason: " + stream.validationErrorMsg;
    }
}

//
// build scanner
//
if (!stream.getScanner("mytrendingtheory")) { // the name of the scanner and the patterns inside the scanner are in the namespace of the stream
    let scanner = stream.createScanner();
    scanner.addConstraint("constraint10sec", // constraint name
        function (curSequence) {
            return curSequence.records[curSequence.records.length - 1].timestamp - curSequence.records[0].timestamp <= 10000;
        },
        "10 second record sets" // explanation
    );
    scanner.addConstraint("constraintAtLeast3Records",
        function (curSequence) {
            return curSequence.records.length >= 3;
        },
        "record sets with at least 3 records"
    );
    scanner.addConstraint("constraintGrowingVol",
        function (curSequence) {
            return curSequence.records.every(record => record.ratioStockVol > 1);
        },
        "stock volume should be higher than the average volume"
    );
    scanner.addConstraint("constraintPriceHigher",
        function (curSequence) {
            return curSequence.records.every(record => record.stockCng > 1.5 * record.stockCngAvg);
        },
        "change in Apple's stock price for each record should be more than 50% above the average"
    );
    scanner.addConstraint("constraintIndexRising",
        function (curSequence) {
            return curSequence.records.every(record => record.idxCng > 1.5 * record.idxCngAvg);
        },
        "change in nasdaq 100 index stock price for each record should be more than 50% above the average"
    );
    scanner.addConstraint("constraintEndingBehavior",
        function (curSequence) {
            for (let i = 1; i < curSequence.records.length; i++) {
                if (!curSequence.records[i].stockCng > curSequence.records[i - 1].stockCng)
                    return false;
            }
            return true;
        },
        "change in stock price is preferably higher than its value for the previous record"
    );

    scanner.settings = {
        "schedule": BC.Meta.Period.Min, // or BC.Meta.Period.Once which is the default
        "activation": BC.Utils.ActionAdapters.createJSCallback(function (result) {
            alert("Found trend in apple's stock: " + result.value + "\nExplanation: " + result.explanation);
        }),
        "start": "Thu May 02 2019 11:48:05 GMT+0300 (Israel Daylight Time)", // when to start back-test
        "end": "Thu May 09 2019 11:48:05 GMT+0300 (Israel Daylight Time)", // finish back-test after a week
    };
    if (!scanner.validate()) {
        throw "Scanner not configured properly, reason: " + scanner.validationErrorMsg;
    }

    scanner.save("mytrendingtheory", true); // override if such exists
}

// run our scanner on the stream
stream.scan("mytrendingtheory");

Find the Perfect Ski Vacation

You are run an online travel booking service, and you wish to increase customer engagement with an intelligent ski vacation advisor that will find the best week for a ski vacation while considering the customers’ availability, resort weather, ski lanes availability and the prices of flights and hotels.

Gameplan

Task: Find the best week for a ski vacation where the best times for ski is when there is enough snow, the prices are low and my calendar is free from meetings.

Search for Patterns where:  

  • A one week vacation.
  • All days on my calendar should be free of meetings.
  • Number of holiday days in a week should be at least three.
  • There should be at least 2 and up to 5 days of snow.
  • The average snow depth should be more than 2 meters.
  • The total cost for the hotels should be under 400$ and the hotel can’t be one that the user explicitly specified to exclude.
  • The overall flights’ cost should be under 300$.
Source Code
const bcToken = "my_braincast_token";
const bcSecretKey = "my_secret_api_key";
const bc = BC.createClient(bcToken, bcSecretKey);
const destination = "Aspen";
const source = "Tel-Aviv";
const hotelsToExclude = ["Leonardo"];

//
// define and validate data sources
//
let stream = bc.Datastore.getStream("Vacation.Example.IdealVacationData");
if (!stream) {
    stream = bc.Datastore.defineStream(
        {
            "source": "Vacation.Example.IdealVacationData",//source name
            "adapter": BC.Utils.SourceAdapters.createS3Adapter( // adapter for fetching a CSV file from AWS S3 of the required data prepared from sources of weather reports for past decade and available hotel rooms, available flight seats and personal calender events for the next year
                "AWS AKIAIOSFODNN7EXAMPLE:bWq2s1WEIj+Ydj0vQ697zp+IXMU=", //authorization string
                "vacation", // S3 bucket
                "vacationData.csv", // S3 object
                "CSV" // object type
            ),
            "record": { // provide an object to specify which attributes of the records being fetched from the source should be used (if omitted, all attributes will be used) - each property is an attribute and the value is that attribute's description:
                "dayOfYear":    "the day of the year represented by a number between 1 and 365",
                "depth":        "the average snow depth on the associated day over the past 10 year period",
                "hotelName":    "the name of a hotel at the destination",
                "hotelPrice":   "the price of an available room in hotelName on the associated day",
                "availability": "true if there are no calendar events on the associated day, otherwise false",
                "isHoliday":    "true if the associated day is a holiday, otherwise false",
                "flightPrice":  "the price of a flight to destination on the associated day (will be undefined if there are no available flights on this day)",
                "destination":  "the flight's destination (will be undefined if there are no available flights on this day)"
            },
        }
    );
    if (!stream || !stream.validate()) {
        throw "Example stream cannot be accessed, reason: " + stream.validationErrorMsg;
    }
}

//
// build scanner
//
if (!stream.getScanner("myIdealVacationTheory")) { // the name of the scanner and the patterns inside the scanner are in the namespace of the stream
    let scanner = stream.createScanner();
    scanner.addConstraint("constraint1Week", // constraint name
        function(curSequence) {
            let numOfDays = curSequence.records.length;
            for (let i = 1; i < numOfDays; i++) { // verify that all days are consecutive. This is necessary because consecutive records may correspond to the same day (corresponding to different hotels/flights) or to days that aren't consecutive (recall that the stream will not posses a record for days on which there were no available hotel rooms)
                let numOfDaysBetweenRecords = curSequence.records[i].dayOfYear-curSequence.records[i-1].dayOfYear;
                if(numOfDaysBetweenRecords !== 1 ||
                   numOfDaysBetweenRecords !== -364) // in case curSequence.records[i] corresponds to January 1st and curSequence.records[i-1] corresponds to December 31
                {
                    return false;
                }
            }
            return  numOfDays === 7; // vacation must be seven days long
        },
        "sequences corresponding to 7 consecutive days"// explanation
    );
    scanner.addConstraint("constraintCalendarAvailability",
        function(curSequence) {
            return  curSequence.records.every(record => record.availability);
        },
        "calender availability for all days in sequence"
    );
    scanner.addConstraint("constraintAtLeast3Holidays",
        function(curSequence) {
            let numOfHolidays = 0;
            for (let i = 1; i < curSequence.records.length; i++) {
                if (curSequence.records[i].isHoliday)
                    numOfHolidays++;
            }
            return numOfHolidays >= 3;
        },
        "should be at least 3 days corresponding to holidays"
    );
    scanner.addConstraint("constraintSnowfall",
        function(curSequence) {
            let numOfDaysWithSnowfall = curSequence.records.filter(record => record.depth > 0).length;
            return numOfDaysWithSnowfall >= 2 && numOfDaysWithSnowfall < 5;
        },
        "number of days with snowfall should be at least 2 and less than 5"
    );
    scanner.addConstraint("constraintAvgDepth",
        function(curSequence) {
            let avgDepth = 0;
            curSequence.records.forEach(record => avgDepth += record.depth);
            avgDepth /= curSequence.records.length;
            return avgDepth > 2;
        },
        "average depth for all days should be higher than 2 meters"
    );
    scanner.addConstraint("constraintHotel",
        function(curSequence) {
            let hotelName = curSequence.records[0].hotelName;
            if(hotelsToExclude.includes(hotelName))return false;
            let hotelPriceForAllDays = 0;
            for (let i = 0; i < curSequence.records.length; i++) {
                let record = curSequence.records[i];
                if(record.hotelName !== hotelName){  // must be same hotel on all days
                    return false;
                }
                hotelPriceForAllDays += record.hotelPrice
            }
            return hotelPriceForAllDays < 400;
        },
        "same hotel, which can't be any of "+hotelsToExclude+", must be available for all days and the total price should be under 400$"
    );
    scanner.addConstraint("constraintFlights",
        function(curSequence) {
            let firstDay = curSequence.records[0];
            let lastDay = curSequence.records[curSequence.records.length-1];
            return  firstDay.flightPrice && firstDay.destination === destination && // there must be an available flight to the destination on the first day
                    lastDay.flightPrice && lastDay.destination === source && // there must be an available flight back on the last day
                    firstDay.flightPrice + lastDay.flightPrice < 300;
        },
        "there must be a flight to the destination on the first day and a flight back to the source on the last day, such that the total trip cost is under 300$"
    );

    scanner.settings = {
        "schedule" : BC.Meta.Period.Day, // or BC.Meta.Period.once which is the default
        "activation" : BC.Utils.ActionAdapters.createJSCallback(function(result) {
            alert("Found your ideal vacation: "+ result+"\nExplanation: "+result.explanation);
        }),
        "start" : "Thu May 02 2019 11:48:05 GMT+0300 (Israel Daylight Time)", // when to start back-test
        "end" : "Thu May 09 2019 11:48:05 GMT+0300 (Israel Daylight Time)", // finish back-test after a week
    };
    if (!scanner.validate()) {
        throw "Scanner not configured properly, reason: "+scanner.validationErrorMsg;
    }
    scanner.save("myIdealVacationTheory", true); // override if such exists
}

// run our scanner on the stream
stream.scan("myIdealVacationTheory");

Send My Tweet at the Perfect Time!

You are the product manager of a social media management platform and wish to create an intelligent tweeter auto-scheduling feature. You realize that if you add smart tweeting based on the level of daily activity of your followers, you can optimize tweet exposure and increase your customers’ users engagement level with their followers.

Gameplan

Task: Analyze the behavior of your followers over the past week and find the best daily 14 half-hour slots to tweet.

Search for Patterns where:

  • Analyze 5-minute intervals within half-hour windows.
  • The number of active followers increases in at least 3 five minute intervals.
  • Window must contain at least one five minute interval in which the amount of active followers is above 75% of the maximum during the past week.
  • Window must contain at least one five minute interval in which the number of activities is above 75% of the maximum during the past week.
Source Code
const bcToken = "my_braincast_token";
const bcSecretKey = "my_secret_api_key";
const bc = BC.createClient(bcToken, bcSecretKey);
const followers = Examples.Twitter.myFollowers;

const twitterAdapterTemplate = {
	openAPISpec: Examples.OpenAPI.JSON.Twitter, // the Open API Specification (OAS) for our REST data source
	path:"/query",  // the HTTP endpoint to use from the OAS spec
	operation:"get",  // the HTTP method to use
	params:{     // required values for parameters specified in the OAS
		"function":   "statuses/user_timeline",
		"apikey":     "AKIAIOSFODNNSLOE:Yxg83MZaEgh3OZ3l0rLo5RTX11o="
	}
};

//
// define and validate data sources
//
let inputSources = {};
let today = new Date();
let pastWeekFilter = {"where":"created_at > "+today.getTime()+" - 1000*60*60*24*7"};
let stream = bc.Datastore.getStream("Twitter.Example.Activity");
if (!stream) {
    stream = bc.Datastore.defineStream(
        {
            "source": "Twitter.Example.Activity",//source name
            "adapter": BC.Utils.SourceAdapters.createREST(twitterAdapterTemplate, {"params": {"follower_ids": followers}}),// Fetch data for list of follower ids
            "record": { // provide an object to specify which attributes of the records being fetched from the source should be used (if omitted, all attributes will be used) - each property is an attribute and the value is that attribute's description:
                "FiveMinuteIntervalOfWeek": "a value in the range [1,500] representing a 5 minute interval of the analyzed 7 day period, where [day1 at 00:00 - day1 at 00:05] is represented by 1 and [day7 at 23:55 - day7 at 24:00] is represented by 500",
                "ActivitiesCount": "the activity level measured as the number of followers that were active during the associated FiveMinuteIntervalOfWeek",
                "ActiveFollowers": "the number of distinct followers that were active during the associated FiveMinuteIntervalOfWeek",
                "ActivitiesCountMax": "the maximum ActivitiesCount encountered during the analyzed 7 day period",
                "ActiveFollowersMax": "the maximum ActiveFollowers encountered during the analyzed 7 day period"
            },
            "filter": pastWeekFilter, // the filter for this source specifying that only the data for the past week should be consumed
            "rate": BC.Meta.Period.Day  // the input consumption rate specifying that all available data from the input source should be consumed once every day - can be BC.Meta.Period.Event for continuous consumption of records as they are generated or BC.Meta.Period.Once which is the default.
        });
    if (!stream || !stream.validate()) {
        throw "Followers activity stream in Twitter example feed cannot be accessed, reason: " + stream.validationErrorMsg;
    }
}

//
// build scanner
//
if (!stream.getScanner("myBestTimeToTweetTheory")) { // the name of the scanner and the patterns inside the scanner are in the namespace of the stream
	let twitterActivityScanner = stream.createScanner();
	twitterActivityScanner.addConstraint("constraint30min", // constraint name
		function(curSequence) {
			return  curSequence.records[curSequence.records.length-1].FiveMinuteIntervalOfWeek - curSequence.records[0].FiveMinuteIntervalOfWeek <= 6;
		},
		"30 minutes size sequences" // explanation
	);
	twitterActivityScanner.addConstraint("constraintGrowingActiveFollowerCountWithAtLeast3Records",
		function(curSequence) {
			for (let i = 1; i < curSequence.records.length; i++) {
				if (curSequence.records[i].ActiveFollowers <= curSequence.records[i - 1].ActiveFollowers)
					return false;
			}
			return  curSequence.records.length >= 3;
		},
		"sequence should have at least 3 five minute intervals with an increasing number of active followers"
	);
	twitterActivityScanner.addConstraint("constraintAtLeast1ActiveFollowerAbove75%Max",
		function(curSequence) {
			return curSequence.records.some(record => record.ActiveFollowers > 0.75 * record.ActiveFollowersMax);
		},
		"amount of active followers for at least one record is above 75% of maximum of the week"
	);
	twitterActivityScanner.addConstraint("constraintAtLeast1ActivityCountAbove75%Max",
		function(curSequence) {
			return curSequence.records.some(record => record.ActivitiesCount > 0.75 * record.ActivitiesCountMax);
		},
		"amount of activities for at least one record is above 75% of maximum of the week"
	);

	twitterActivityScanner.settings = {
		"schedule" : BC.Meta.Period.Day, // or BC.Meta.Period.Once which is the default
		"activation" : BC.Utils.ActionAdapters.createJSCallback(function(result) {
			alert("Found the best time to tweet: "+ result+"\nExplanation: "+result.explanation);
		}),
		"start" : "Thu May 02 2019 11:48:05 GMT+0300 (Israel Daylight Time)", // when to start back-test
		"end" : "Thu May 09 2019 11:48:05 GMT+0300 (Israel Daylight Time)", // finish back-test after a week
	};
	if (!twitterActivityScanner.validate()) {
		throw "Scanner not configured properly, reason: "+twitterActivityScanner.validationErrorMsg;
	}

	twitterActivityScanner.save("myBestTimeToTweetTheory", true); // override if such exists
}

// run our scanner on stream
stream.scan("myBestTimeToTweetTheory");

Drug Designers Delight

You head an R&D division in a large pharmaceutical company that specializes in drug design. Your researchers often waste precious time and resources pursuing potential anti-influenza NA antigens that fail to neutralize different subtypes of the target viral protein efficiently. You want to find functionally conserved sites across different subtypes with additional factors such as solvent accessibility or steric interference. 

Gameplan

Task: Find the characteristic functional site of the influenza NA protein family where influenza NA protein family share similar amino acid sequences combination. Analyze the 3D structures and the amino acid sequences of influenza NA protein family members. Search for a combination of three amino acids that occur in the exact same order in at least 90% of the proteins, and which are spatially located up to 4 angstroms from one another. Source is Protein Data Bank (PDB) records for each atom of every protein, containing the 3D coordinates, the containing amino acid’s name and position along the protein sequence’s, the containing protein chain and PDB identifier of the protein.

Search for Patterns where:  

  • Find subsequences of 3 amino acids.
  • Occurs in at least 90% of all proteins.
  • Amino acids belong to the same protein.
  • The largest spatial distance between the centroid atoms of every two amino acids (diameter) must be no more than 4 angstroms.
Source Code
const token = "my_braincast_token";
const secretKey = "my_secret_api_key";
const bc = BC.createClient(token, secretKey);
// A list of PDB ids for the proteins that represent the 10 different influenza NA subtypes:
const proteinFamilyFromUserRequest = ["3B7E", "2AEP", "4HZV", "2HTV", "3SAL", "4QN4", "4QN3", "4WA3", "4MWJ", "4GDJ"];

//
// define and validate data sources
//
let stream = bc.Datastore.getStream("Influenza.Example.proteinDataset");
if (!stream) {
    stream = bc.Datastore.defineStream(
        {
            "source": "Influenza.Example.proteinDataset",//source name
            "adapter": BC.Utils.SourceAdapters.createS3Adapter( // adapter for fetching a CSV file containing amino acid (AA) sequence and 3D info for many NA proteins, from AWS S3
                "AWS AKIAIOSFODNN7EXAMPLE:bWq2s1WEIj+Ydj0vQ697zp+IXMU=", //authorization string
                "proteins", // S3 bucket
                "NA.csv", // S3 object
                "CSV" // object type
            ),
            "record": { // provide an object to specify which attributes of the records being fetched from the source should be used (if omitted, all attributes will be used) - each property is an attribute and the value is that attribute's description:
                "PdbID": "the identifier of the protein that the amino acid represented by this record is associated with",
                "AAPosition": "the position of the amino acid within its associated protein",
                "AAName": "the name of the amino acid",
                "XCent": "the x-coordinate of the the amino acid's centroid atom",
                "YCent": "the y-coordinate of the the amino acid's centroid atom",
                "ZCent": "the z-coordinate of the the amino acid's centroid atom"
            },
            "filter": {"where": "PdbID IN (" + proteinFamilyFromUserRequest.join(',') + ") AND Chain = 'A'"}, // the filter for this source specifying that only the data for proteins in the specified set should be consumed
            "order": ["AAPosition"]// output ordering - order the amino acid records according to their order within their associated protein's sequence
        }
    );
    if (!stream || !stream.validate()) {
        throw "Cannot access data source, reason: " + stream.validationErrorMsg;
    }
}

//
// build scanner
//
if (!stream.getScanner("myFunctionalSiteDetectionTheory")) { // the name of the scanner and the patterns inside the scanner are in the namespace of the stream
    let scanner = stream.createScanner();
    scanner.addConstraint("constraintAminoAcidTriads", // constraint name
        function (curSequence) {
            return curSequence.records.length === 3 &&
                curSequence.records.every(record => record.PdbID === curSequence.records[0].PdbID);
        },
        "must be a sequence of three amino acids (triads) within a protein" // explanation
    );
    scanner.addConstraint("constraintFunctionalSiteDiameter",
        function (curSequence) {
            for (let i = 0; i < curSequence.records.length; i++) {
                for (let j = i + 1; i < curSequence.records.length; i++) {
                    let aa1 = curSequence.records[i];
                    let aa2 = curSequence.records[j];
                    // Calculate the 3D distance between the current two AA in this sequence:
                    let distance = Math.sqrt(
                        Math.pow(aa2.XCent - aa1.XCent, 2) +
                        Math.pow(aa2.YCent - aa1.YCent, 2) +
                        Math.pow(aa2.ZCent - aa1.ZCent, 2));
                    if (distance > 4e-10) {
                        return false;
                    }
                }
            }
            return true;
        },
        "all amino acids must be within a 4 angstrom spatial diameter"
    );
    scanner.addConstraint("constraintMinimumSupport",
        function (curSequence) {
            // Partition stream into record sequences, one for each unique PdbID, i.e. one for each protein:
            let proteins = BC.Meta.Functions.Streams.createPartition(
                stream, // stream associated with the scanner
                ["PdbID"] // the attributes by which to partition the stream (i.e. all records with the same PdbID will be assigned to the same sequence).
            );
            // Use the support function to count the number of protein sequences whose AAName subsequences contain the current sequence's AAName subsequence (at least once):
            return BC.Meta.Functions.Sequences.support(
                curSequence,
                proteins,
                ["AAName"] // the attributes to consider when checking for containment
            ) >= 0.9 * proteins.length;
        },
        "should occur in at least 90% of the proteins"
    );

    if (!scanner.validate()) {
        throw "Scanner not configured properly, reason: " + scanner.validationErrorMsg;
    }

    scanner.save("myFunctionalSiteDetectionTheory", true); // override if such exists
}

// run our scanner on the stream
result = stream.scan("myFunctionalSiteDetectionTheory");
alert("Found the following functional site in NA family: " + result + "\nExplanation: " + result.explanation);


 

Early Access

Email address is wrong, please try again

Thanks for subscribing. We will send an email when your invite is ready!

Spread the word about Braincast ❤️