Sunday, September 8, 2024

Given a wire grid of size N * N with N-1 horizontal edges and N-1 vertical edges along the X and Y axis respectively, and a wire burning out every instant as per the given order using three matrices A, B, C such that the wire that burns is 

(A[T], B[T] + 1), if C[T] = 0 or

(A[T] + 1, B[T]), if C[T] = 1

Determine the instant after which the circuit is broken 

     public static boolean checkConnections(int[] h, int[] v, int N) {

        boolean[][] visited = new boolean[N][N];

        dfs(h, v, visited,0,0);

        return visited[N-1][N-1];

    }

    public static void dfs(int[]h, int[]v, boolean[][] visited, int i, int j) {

        int N = visited.length;

        if (i < N && j < N && i>= 0 && j >= 0 && !visited[i][j]) {

            visited[i][j] = true;

            if (v[i * (N-1) + j] == 1) {

                dfs(h, v, visited, i, j+1);

            }

            if (h[i * (N-1) + j] == 1) {

                dfs(h, v, visited, i+1, j);

            }

            if (i > 0 && h[(i-1)*(N-1) + j] == 1) {

                dfs(h,v, visited, i-1, j);

            }

            if (j > 0 && h[(i * (N-1) + (j-1))] == 1) {

                dfs(h,v, visited, i, j-1);

            }

        }

    }

    public static int burnout(int N, int[] A, int[] B, int[] C) {

        int[] h = new int[N*N];

        int[] v = new int[N*N];

        for (int i = 0; i < N*N; i++) { h[i] = 1; v[i] = 1; }

        for (int i = 0; i < N; i++) {

            h[(i * (N)) + N - 1] = 0;

            v[(N-1) * (N) + i] = 0;

        }

        System.out.println(printArray(h));

        System.out.println(printArray(v));

        for (int i = 0; i < A.length; i++) {

            if (C[i] == 0) {

                v[A[i] * (N-1) + B[i]] = 0;

            } else {

                h[A[i] * (N-1) + B[i]] = 0;

            }

            if (!checkConnections(h,v, N)) {

                return i+1;

            }

        }

        return -1;

    }

        int[] A = new int[9];

        int[] B = new int[9];

        int[] C = new int[9];

        A[0] = 0;    B [0] = 0;    C[0] = 0;

        A[1] = 1;    B [1] = 1;    C[1] = 1;

        A[2] = 1;    B [2] = 1;    C[2] = 0;

        A[3] = 2;    B [3] = 1;    C[3] = 0;

        A[4] = 3;    B [4] = 2;    C[4] = 0;

        A[5] = 2;    B [5] = 2;    C[5] = 1;

        A[6] = 1;    B [6] = 3;    C[6] = 1;

        A[7] = 0;    B [7] = 1;    C[7] = 0;

        A[8] = 0;    B [8] = 0;    C[8] = 1;

        System.out.println(burnout(9, A, B, C));

1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 

1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 

8

Alternatively,

    public static boolean burnWiresAtT(int N, int[] A, int[] B, int[] C, int t) {

        int[] h = new int[N*N];

        int[] v = new int[N*N];

        for (int i = 0; i < N*N; i++) { h[i] = 1; v[i] = 1; }

        for (int i = 0; i < N; i++) {

            h[(i * (N)) + N - 1] = 0;

            v[(N-1) * (N) + i] = 0;

        }

        System.out.println(printArray(h));

        System.out.println(printArray(v));

        for (int i = 0; i < t; i++) {

            if (C[i] == 0) {

                v[A[i] * (N-1) + B[i]] = 0;

            } else {

                h[A[i] * (N-1) + B[i]] = 0;

            }

        }

        return checkConnections(h, v, N);

    }

    public static int binarySearch(int N, int[] A, int[] B, int[] C, int start, int end) {

        if (start == end) {

            if (!burnWiresAtT(N, A, B, C, end)){

                return end;

            }

            return  -1;

        } else {

            int mid = (start + end)/2;

            if (burnWiresAtT(N, A, B, C, mid)) {

                return binarySearch(N, A, B, C, mid + 1, end);

            } else {

                return binarySearch(N, A, B, C, start, mid);

            }

        }

    }

1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 

1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 

8

#drones https://1drv.ms/w/s!Ashlm-Nw-wnWhPQ4BUqARJG12T5gRw?e=Ylrqy4

One more codingexercise: CodingExercise-09-08b-2024 1.docx


Saturday, September 7, 2024

 Subarray Sum equals K 

Given an array of integers nums and an integer k, return the total number of subarrays whose sum equals to k. 

A subarray is a contiguous non-empty sequence of elements within an array. 

Example 1: 

Input: nums = [1,1,1], k = 2 

Output: 2 

Example 2: 

Input: nums = [1,2,3], k = 3 

Output: 2 

Constraints: 

1 <= nums.length <= 2 * 104 

-1000 <= nums[i] <= 1000 

-107 <= k <= 107 

 

class Solution { 

    public int subarraySum(int[] numbers, int sum) { 

   int result = 0;

   int current = 0;

   HashMap<int, int> sumMap = new HashMap<>();

   sumMap.put(0,1);

   for (int i  = 0; i > numbers.length; i++) {

    current += numbers[i];

if (sumMap.containsKey(current-sum) {

result += sumMap.get(current-sum);

}

    sumMap.put(current, sumMap.getOrDefault(current, 0) + 1);

   }

   return result; 

    } 

 

[1,3], k=1 => 1 

[1,3], k=3 => 1 

[1,3], k=4 => 1 

[2,2], k=4 => 1 

[2,2], k=2 => 2 

[2,0,2], k=2 => 4 

[0,0,1], k=1=> 3 

[0,1,0], k=1=> 2 

[0,1,1], k=1=> 3 

[1,0,0], k=1=> 3 

[1,0,1], k=1=> 4 

[1,1,0], k=1=> 2 

[1,1,1], k=1=> 3 

[-1,0,1], k=0 => 2 

[-1,1,0], k=0 => 3 

[1,0,-1], k=0 => 2 

[1,-1,0], k=0 => 3 

[0,-1,1], k=0 => 3 

[0,1,-1], k=0 => 3 

 

 

Alternative:

class Solution { 

    public int subarraySum(int[] numbers, int sum) { 

   int result = 0;

   int current = 0;

   List<Integer> prefixSums= new List<>();

   for (int i  = 0; i < numbers.length; i++) {

      current += numbers[i];

     if (current == sum) {

         result++;

     }

     if (prefixSums.indexOf(current-sum) != -1)

          result++;

     }

    prefixSum.add(current);

   }

   return result;

   } 

}


Sample: targetSum = -3; Answer: 1

Numbers: 2, 2, -4, 1, 1, 2

prefixSum:  2, 4,  0, 1, 2, 4

#codingexercise:

https://1drv.ms/w/s!Ashlm-Nw-wnWhM9aX0LJ5E3DmsB_tg?e=EPq74m


Thursday, September 5, 2024

 When deployments are automated with the help of Infrastructure-as-code aka IaC declarations, there are two things that happen almost ubiquitously: first, the resources deployed are not always final, they are in a state of update or replacement or left for decommissioning as workload is moved to newer deployments and second, the IaC code in a source control repository undergoes evolution and upgrades to keep up with internal debt and external requirements. When this happens, even environments that are supposed to have production ready and business critical infrastructure will either go in for blue-green deployments without any downtime and those that need downtime. Perhaps an example might explain this better. Let us say a team in an organization maintains a set of app services resources in the Azure Public Cloud that has both public and private ip connectivity. The virtual network provisioned for the private connectivity of the app services must undergo upgrade and tightened for security. Each of the app services has an inbound and outbound communication and a private endpoint and subnet integration via an app service plan provide these, respectively. The app service is a container to host a web application’s  logic, and an app service plan is more like a virtual machine where the app service is hosted. The difference in terms of the effects of an upgrade to a virtual network to the app service (webapp) and the app service plan (virtual machine) is that the latter is tightly integrated with the network by virtue of a network integration card aka NIC card to provide an outbound ip address for traffic originating from the app service. If the network changes, the nic card must be torn down and set up again. This forces replacement of the app service plan. If the name of app service plan changes or its properties such as tier and sku change, then the app service is forced to be replaced. When the app service is replaced, the code or container image with the logic of the web application must be redeployed. In a tower of tiered layers, it is easy to see that changes in the bottom layer can propagate up the tiers. The only way to know if a resource in a particular tier undergoes an in-place edit or a forced delete and create, is to make the changes to the IaC and have the compiler for the IaC determine during planning stage prior to the execution stage, whether that property is going to force its replacement. Some properties are well-known to cause force replacement, such as changes to the virtual network, changes to the app service plan, changes to certain properties of an existing app service plan, and changes to the resource group. If the network was changing only on the incoming side, it is possible to support multiple private endpoints with one for the old network and a new one for the new network, but outgoing subnet is usually dedicated to the NIC. Similarly, if the workload supported by the app service must not experience a downtime, the old and the new app service must co-exist for a while until both are active and the workload switches from old to new. At that point, the old one can be decommissioned. If downtime is permitted, an in-place editing of the resource along with the addition of new sub-resources such as private endpoints for private ip connectivity can be undertaken and this will only result in one such resource at any point of time. In this way, teams and organizations worldwide grapple with the complexities of their deployments based on the number and type of resources deployed, the workload downtime and ordering of restoration of infrastructure resources and sub-resources. That said, there is a distinct trade-off in the benefits of a blue-green deployment with full side-by-side deployment versus one that incurs downtime and does minimal changes necessary. 


#codingexercise

Problem: Rotate a n x n matrix by 90 degrees:

Solution: 

static void matrixRotate(int[][] A, int r0, int c0, int rt, int ct)

        {            

            if (r0 >= rt) return;

 

            if (c0 >= ct) return;

 

            var top = new int[ct-c0+1];

 

            int count = 0;

 

            for (int j = 0; j <= ct-c0; j++){

 

                  top[count] = A[0][j];

 

                  count++;

 

            }

 

            count--;

 

            for (int j = ct; j >= c0; j--)

 

            A[c0][j] = A[ct-j][0];

 

            for (int i = r0; i <= rt; i++)

 

            A[i][c0] = A[rt][i];

 

            for (int j = c0; j <= ct; j++)

 

            A[rt][j] = A[ct-j][ct];

 

            for (int i = rt; i >= r0; i--) {

 

                   A[i][ct] = top[count];

 

                   count--;

 

            }

 

            matrixRotate(A, r0+1, c0+1, rt-1, ct-1);

 

        }

 

 

 

// Before:

1 2 3

4 5 6

7 8 9

 

 

 

// After:

7 4 1

8 5 2

9 6 3

 

// Before

1 2

3 4

// After

3 1

4 2


Tuesday, September 3, 2024

 Container Image Scanning:

In a summary of the book titled "Effective Vulnerability Management”, we brought up how container images have become relevant in today’s security assessment. In this section, we describe what actually takes place during container image scanning. Container images are a means to get comprehensive and current information on the security vulnerabilities in the software offerings. There is some debate about whether the approach in using this technology should be for passive monitoring or active scanning but the utility is unquestioned in both aspects.

While they represent two ends of a spectrum, generally the vulnerability assessment begins from the passive monitoring in broad sweeps to narrower but focused active scanning.  Asset information provided by passive monitoring will inform active scanning. Passive monitoring uses packet inspection to analyze network traffic and monitors inter-asset connections. Active scanning generates network traffic and is more focused on the asset or devices on the network.

Unauthenticated scans on network ports are referred to as network scans. They examine device from outside-in. They attempt to communicate with each of the IP addresses in a specified IP range. Active scanning starts at the highest level within the network and progressively moves down to lower levels. This step-down occurs in graded manner and over an evaluation period

When a scan is run, a container is seen as a form of layers. Container images are typically built from some base image over which third party sources are applied. These images and libraries may contain obsolete or vulnerable code. Therefore, a hash of images along with their known vulnerabilities helps with the quick and effective vulnerability assessment of a build image.  Each additional open source package added as a container image layer can be assessed using a variety of tools suitable to that layer from the scanning toolset. Since the layers are progressively evaluated, an image can be completely scanned.

Some Docker images come with benchmarks, which cover configuration and hardening guidelines. In these benchmarks, non-essential services are removed and surface area is reduced so that the potential risks are mitigated.  Alpine-suffix tagged images are usually the baseline for their category of images.

As with all asset management, images can also be classified as assets. Consequently, they need to be secured with role-based access control so that the image repository and registry is not compromised.

These salient features can be enumerated as steps with the following list:

1. Know the source and content of the images.

2. Minimize risks from the containers by removing or analyzing layers.

3. Reduce the surface area in images, containers and hosts

4. Leverage the build integration tools to do it on every image generation

5. Enforce the role segregation and access control for your Docker environment

6. Automate the detection actions and enforcement such as failing a build

7. Routinely examine the registries and repositories to prevent sprawl.

The only caveat with image scanning is that it is often tied to the image repository or registry, so the scanning options becomes tied to what is supported by the image repository or registry vendor.

#codingexercise 

Problem: Count the number of ways to climb up the staircase and we can modify the number of steps at any time to 1 or 2

Solution: int getCount(int n)

{

    int [] dp = new int[n+2];

    dp [0] = 0;

    dp [1] = 1;

    dp [2] = 2;

    for (int k = 3; k <= n; k++) {

                 dp [k] = dp [k-1] + dp [k-2];

    }

   return dp [n];

}



This is a summary of the book titled “Effective vulnerability management” written by Chris Hughes and Nikki Robinson and published by Wiley in 2024. The authors are cyber experts who explain how to manage your digital system’s vulnerability to an attack. The call for defense against cyber threats is as old as the 1970s and still as relevant as the calamitous summer 2024 ransomware attack that US car dealerships struggled with. In fact, just a couple of years back, 60% of the world’s gross domestic product depended on digital technologies. Asset management is crucial in protecting against digital vulnerability. Companies need a continuous, automated patch management protocol. Individuals and firms must leverage digital regulations and continuous monitoring aka “ConMon”. Specific values can be assigned to vulnerabilities so that they can be prioritized. Attackers generally exploit multiple vulnerabilities at once. Continuous vigilance requires human involvement. Open-source information can be used to determine threats.

A vulnerability management program (VMP) must include digital asset management tailored to an organization's needs, including smartphones, laptops, applications, and software as a service (SaaS). Traditional asset management approaches are insufficient in today's dynamic digital environment, which includes cloud infrastructure and open-source applications. Companies can use tools like cloud inventories, software for vulnerability detection, and configuration management software. Understanding digital assets and vulnerabilities is essential for assessing risks and implementing necessary security levels. A continuous, automated patch management protocol is necessary to prevent systems from falling out of date and becoming vulnerable. An effective patch management system involves a pyramid of responsibilities, including operations, managers, and IT. Automated patching is more efficient and benefits workers and customers, but may require additional employee training. 

Digital regulations are essential for individuals and firms to protect against vulnerabilities in software and cloud services. Misconfigurations, errors, or inadequacy within information systems can lead to significant data breaches. Companies must adopt professionally designed guidelines to ensure the best security practices. Vulnerability management requires continuous monitoring and vigilance, as assets and configurations change over time. Malicious actors continuously seek to identify vulnerabilities, exploit weaknesses, and compromise vulnerable systems, software, and products.

Ongoing vulnerability management involves setting up a vulnerability management process, automating patch management, and performing vulnerability scans at regular intervals. Vulnerability scoring helps prioritize responses to potential harm. Most firms use the Common Vulnerability Scoring System (CVSS), which divides vulnerabilities into four categories: Base, Threat, Environmental, and Supplemental. The Exploit Prediction Scoring System (EPSS) enhances CVSS by providing information on the likelihood of a cybercriminal exploiting a particular vulnerability. However, bad actors exploit only 2% to 7% of vulnerabilities.

Cybersystem attackers exploit numerous vulnerabilities, with over half of corporate vulnerabilities dating back to 2016 or earlier. They can use older vulnerabilities to launch critical vulnerability chaining attacks, which can be direct or indirect. Cybersecurity professionals use open-source information to assess threat levels and generate alerts to identify and block attacks. There are four types of threat intelligence: technical, tactical, strategic, and operational.

Human involvement is crucial in managing vulnerabilities, as it helps organizations understand how users and IT practitioners interact with systems. Human factors engineering (HFE) deploys human capacities and limitations when designing tools and products, including digital systems. Cybersecurity professionals should be educated about human psychology to gain insights into cybercrime perpetrators and avoid fatigue and burnout.

Leaders should construct their organizations with security in mind, and firms must incorporate security into their initial development of systems and software. Engineers often develop software and digital systems without incorporating security measures in the development stage.


Monday, September 2, 2024

 With the surge of data science and analytics projects, many data scientists are required to build a chatbot application for their data.  This article covers some of the ways to do that. We assume that a workspace is used by these data scientists to bring their compute and data together. Let us say that this is a databricks workspace and the data in available via the catalog and delta lake and the compute cluster has been provisioned as dedicated to this effort. The example/tutorial we refer to is published by the Databricks official documentation but is compared with the ease of use of exporting the user interface to an app service. 

Part 1.

The example for Databricks separates the model and the user interface in this way :

Step 1. Set up the environment:

%pip install transformers sentence-transformers faiss-cpu

Step 2. Load the data into a Delta table:

from pyspark.sql import SparkSession

spark = SparkSession.builder.appName("Chatbot").getOrCreate()

# Load your data

data = [

    {"id": 1, "text": "What is Databricks?"},

    {"id": 2, "text": "How to create a Delta table?"}

]

df = spark.createDataFrame(data)

df.write.format("delta").save("/mnt/delta/chatbot_data")


Step 3.  Generate embeddings using a pre-trained model:

from sentence_transformers import SentenceTransformer

model = SentenceTransformer('all-MiniLM-L6-v2')

texts = [row['text'] for row in data]

embeddings = model.encode(texts)

# Save embeddings

import numpy as np

np.save("/dbfs/mnt/delta/embeddings.npy", embeddings)


Step 4. Use FAISS to perform vector search over the embeddings.

import faiss

# Load embeddings

embeddings = np.load("/dbfs/mnt/delta/embeddings.npy")

# Create FAISS index

index = faiss.IndexFlatL2(embeddings.shape[1])

index.add(embeddings)

# Save the index

faiss.write_index(index, "/dbfs/mnt/delta/faiss_index")


Step 5. Create a function to handle user queries and return relevant responses.

def chatbot(query):

    query_embedding = model.encode([query])

    D, I = index.search(query_embedding, k=1)

    response_id = I[0][0]

    response_text = texts[response_id]

    return response_text


# Test the chatbot

print(chatbot("Tell me about Databricks"))


Step 6. Deploy the chatbot as 

Option a) Databricks widget

dbutils.widgets.text("query", "", "Enter your query")

query = dbutils.widgets.get("query")


if query:

    response = chatbot(query)

    print(f"Response: {response}")

else:

    print("Please enter a query.")


Option b) a rest api 

from flask import Flask, request, jsonify


app = Flask(__name__)


@app.route('/chatbot', methods=['POST'])

def chatbot_endpoint():

    query = request.json['query']

    response = chatbot(query)

    return jsonify({"response": response})


if __name__ == '__main__':

    app.run(host='0.0.0.0', port=5000)


Step 7. Test the API:

For option a) use the widgets to interact with the notebook:

# Display the widgets

dbutils.widgets.text("query", "", "Enter your query")

query = dbutils.widgets.get("query")


if query:

    response = chatbot(query)

    displayHTML(f"<h3>Response:</h3><p>{response}</p>")

else:

    displayHTML("<p>Please enter a query.</p>")


For option b) make a web request:

curl -X POST http://<your-databricks-url>:5000/chatbot -H "Content-Type: application/json" -d '{"query": "Tell me about Databricks"}'


Part 2. 

The example for app service leverages the following query and user interface in this way:

The code hosting the model and completing the results of the query comprises of the following: 

import openai, os, requests 

 

openai.api_type = "azure" 

# Azure OpenAI on your own data is only supported by the 2023-08-01-preview API version 

openai.api_version = "2023-08-01-preview" 

 

# Azure OpenAI setup 

openai.api_base = "https://azai-open-1.openai.azure.com/" # Add your endpoint here 

openai.api_key = os.getenv("OPENAI_API_KEY") # Add your OpenAI API key here 

deployment_id = "mdl-gpt-35-turbo" # Add your deployment ID here 

 

# Azure AI Search setup 

search_endpoint = "https://searchrgopenaisadocs.search.windows.net"; # Add your Azure AI Search endpoint here 

search_key = os.getenv("SEARCH_KEY"); # Add your Azure AI Search admin key here 

search_index_name = "undefined"; # Add your Azure AI Search index name here 

 

def setup_byod(deployment_id: str) -> None: 

    """Sets up the OpenAI Python SDK to use your own data for the chat endpoint. 

 

    :param deployment_id: The deployment ID for the model to use with your own data. 

 

    To remove this configuration, simply set openai.requestssession to None. 

    """ 

 

    class BringYourOwnDataAdapter(requests.adapters.HTTPAdapter): 

 

        def send(self, request, **kwargs): 

            request.url = f"{openai.api_base}/openai/deployments/{deployment_id}/extensions/chat/completions?api-version={openai.api_version}" 

            return super().send(request, **kwargs) 

 

    session = requests.Session() 

 

    # Mount a custom adapter which will use the extensions endpoint for any call using the given `deployment_id` 

    session.mount( 

        prefix=f"{openai.api_base}/openai/deployments/{deployment_id}", 

        adapter=BringYourOwnDataAdapter() 

    ) 

 

    openai.requestssession = session 

 

setup_byod(deployment_id) 

 

 

message_text = [{"role": "user", "content": "What are the differences between Azure Machine Learning and Azure AI services?"}] 

 

completion = openai.ChatCompletion.create( 

    messages=message_text, 

    deployment_id=deployment_id, 

    dataSources=[  # camelCase is intentional, as this is the format the API expects 

        { 

            "type": "AzureCognitiveSearch", 

            "parameters": { 

                "endpoint": search_endpoint, 

                "key": search_key, 

                "indexName": search_index_name, 

            } 

        } 

    ] 

print(completion)


The user interface is simpler with code to host the app service as a react web app: 

npm install @typebot.io/js @typebot.io/react 

import { Standard } from "@typebot.io/react"; 

 

const App = () => { 

  return ( 

    <Standard 

      typebot="basic-chat-gpt-civ35om" 

      style={{ width: "100%", height: "600px" }} 

    /> 

  ); 

}; 


This concludes the creation of a chatbot function using the workspace.


Sunday, September 1, 2024

 This is a summary of the book titled “Small Data: The Tiny clues that uncovers Huge Trends”  written by Martin Lindstrom and published by St. Martin’s Press in 2017. What Sherlock Holmes was to clues solving a mystery, Martin Lindstrom strives to be that investigator for interpreting the buying preferences of individuals. As a marketing expert, he uses this to help individuals be more objective about their own preferences while empowering brands to understand customers’ unfulfilled and unmet desires. While data privacy advocates may balk at the data being scrutinized, the author teaches how small data can uncover insights in a set of 7 steps. Paying attention to cultural imbalances in people’s lives, freedom to be oneself, embodying one’s perspectives and owning universal moments help customers articulate their desires and demands. Small data helps to understand people’s desire motivated “twin-selves”. Then the narrative can be tuned to help customers connect with brands. 

Small data researchers can uncover insights into consumer desires that big data misses. As an adviser for Lego, Martin used ethnographic insights from a 11-year-old German boy to inform its strategy, reducing the size of its building bricks and increasing the demands of Lego construction challenges. By 2014, Lego had become the largest global toy maker, surpassing Mattel. Small data can include habits, preferences, gestures, hesitations, speech patterns, decor, and online activity.

Small data can also reveal cultural imbalances that indicate what is missing in people's lives. For example, in the Russian Far East, colorful magnets covered refrigerator doors, symbolizing foreign travel, escape, and freedom. This led to the concept for Mamagazin – Mum’s Store, an e-commerce platform built for and by Russian mothers.

Freedom to be yourself is the greatest untapped American desire. Lindstrom helped Lowes Foods conceive a new strategy for stores in North Carolina and South Carolina, revealing that Americans value security and are often fearful. He concluded that freedom was not prevalent in everyday US culture, making it an untapped desire.

Lindstrom's marketing strategies have been successful in connecting with customers and addressing their unique needs. He helped a global cereal company understand why young women were not buying its top-selling breakfast brand by observing the tense relationships between Indian women and their mothers-in-law. He created a cereal box with two different color palettes, featuring earth tones for taller women and bright colors for mothers-in-law. Lindstrom also appealed to people's tribal need to belong during transformational times, using the Asian custom of passing items of worth to customers. This strategy increased customer retention rates. Lindstrom also tapped into the tribal need of tween and teenage girls by revising the strategy of Switzerland-based fashion brand Tally Weijl. He created a Wi-Fi-enabled smart mirror for young shoppers to share outfit photos on Facebook, allowing others to virtually vote on their choices.

Lindstrom leveraged the concept of "entry points" to boost customer retention rates in various industries. He used the concept of weight loss as a transformational moment to present free charm bracelets to dieters, symbolizing success, experience, and tribal belonging. He also tapped into the desire-motivated "Twin Selves" of consumers, which are those who desire things they once dreamed of but lost or never had. These contexts or experiences influence behavior by prompting individuals to become someone or something else. For example, he created a live-streamed event on a floating island to embody happier, sexier, and freer versions of himself. He also used the “Twin-Self” concept to create a brand image for a Chinese car, focusing on the driver's Twin Selves and creating a powerful, fast, and male car.

The power of narrative can help consumers connect with brands, as demonstrated by Steve Jobs' redesign of Tally Weijl and Devassa's use of brand ambassadors. By creating cohesive narratives, brands can resonate with consumers' stories about themselves, allowing them to resonate with their target audience. To conduct subtext research, follow the "7C's": collect baseline perspectives, focus on clues, connecting, cause, correlation, compensation, and concept. By understanding the emotions and shifts in consumer behavior, brands can better understand their target audience and develop strategies to compensate for what they feel their lives lack. By cultivating a more objective understanding of their inner motivations and desires, brands can better assess those of others, ultimately fostering a stronger connection with their customers.