Marks: 60
The number of restaurants in New York is increasing day by day. Lots of students and busy professionals rely on those restaurants due to their hectic lifestyles. Online food delivery service is a great option for them. It provides them with good food from their favorite restaurants. A food aggregator company FoodHub offers access to multiple restaurants through a single smartphone app.
The app allows the restaurants to receive a direct online order from a customer. The app assigns a delivery person from the company to pick up the order after it is confirmed by the restaurant. The delivery person then uses the map to reach the restaurant and waits for the food package. Once the food package is handed over to the delivery person, he/she confirms the pick-up in the app and travels to the customer's location to deliver the food. The delivery person confirms the drop-off in the app after delivering the food package to the customer. The customer can rate the order in the app. The food aggregator earns money by collecting a fixed margin of the delivery order from the restaurants.
The food aggregator company has stored the data of the different orders made by the registered customers in their online portal. They want to analyze the data to get a fair idea about the demand of different restaurants which will help them in enhancing their customer experience. Suppose you are hired as a Data Scientist in this company and the Data Science team has shared some of the key questions that need to be answered. Perform the data analysis to find answers to these questions that will help the company to improve the business.
The data contains the different data related to a food order. The detailed data dictionary is given below.
# import libraries for data manipulation
import numpy as np
import pandas as pd
# import libraries for data visualization
import matplotlib.pyplot as plt
import seaborn as sns
from google.colab import drive
drive.mount('/content/drive')
# Copying data to another variable to avoid any changes to the original data
df = data.copy()
# read the data
data = pd.read_csv('/content/drive/MyDrive/week 2/foodhub_order.csv')
# returns the first 5 rows
df.head()
The DataFrame has 9 columns as mentioned in the Data Dictionary. Data in each row corresponds to the order placed by a customer.
# Write your code here
# Use the shape attribute to find the number of rows and columns
num_rows, num_columns = df.shape
print(f"Number of rows: {num_rows}")
print(f"Number of columns: {num_columns}")
Data contains 1898 unique orders based on unique of order
# Use info() to print a concise summary of the DataFrame
df.info()
There are 4 object type columns. There are 4 integer type columns. There is 1 floating number type columns.
# Write your code here
# Check for missing values in the DataFrame
missing_values = df.isnull().sum()
# Display columns with missing values (if any)
print(missing_values[missing_values > 0])
df.describe(include='all').T
The minimum time is 20.0 minutes for food to be prepared once an order is placed. The average time is 27.37 minutes for food to be prepared once an order is placed. The maximum time is 35.0 minutes for food to be prepared once an order is placed. ```
# Write the code here
not_rated_count = (df['rating'] == 'Not given').sum()
print("Number of orders not rated:", not_rated_count)
total_orders = len(df)
percentage_not_rated = (not_rated_count / total_orders) * 100
print(f"Percentage of orders not rated: {percentage_not_rated:.2f}%")
Out of 1898 orders, 736 orders have not been rated. 38.78% of the orders haven't been rated.
# This is formatted as code
# Write the code here
import seaborn as sns
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.pyplot as clt
# Example: Explore 'cost_of_the_order' variable with a histogram
plt.figure(figsize=(8, 6))
sns.histplot(data['cost_of_the_order'], bins=30, kde=True)
plt.title('Distribution of Cost of Orders')
plt.xlabel('Cost of Order')
plt.ylabel('Frequency')
plt.ylim(0,350)
plt.show()
plt.savefig("output.jpg", bbox_inches='tight')
The graph is right/ poistively skewed , it shows that dishes with a cost of the order less than 17 are in high demand. There is a decrease in number of order as cost increases.
# Calculate the mean of the 'delivery_time' column
mean_cost_of_the_order = data['cost_of_the_order'].mean()
# Assuming 'data' contains your DataFrame and 'cost_of_the_order' is a column in it
plt.figure(figsize=(8, 6))
plt.boxplot(data['cost_of_the_order'], patch_artist=True, boxprops=dict(facecolor='lightblue'))
plt.axhline(y=mean_cost_of_the_order, color='red', linestyle='--', label=f'Mean: {mean_cost_of_the_order:.2f} min')
plt.title('Boxplot of Cost of Order')
plt.legend()
plt.show()
# Calculate the mean of the 'cost_of_the_order' column
mean_cost = np.mean(data['cost_of_the_order'])
print(f"Mean cost of the order: {mean_cost:.3f}")
# Calculate the 25th and 75th percentiles
percentile_25 = np.percentile(data['cost_of_the_order'], 25)
percentile_50 = np.percentile(data['cost_of_the_order'], 50)
percentile_75 = np.percentile(data['cost_of_the_order'], 75)
print(f"25th percentile: {percentile_25}")
print(f"50th percentile: {percentile_50}")
print(f"75th percentile: {percentile_75}")
plt.show()
plt.savefig("output.jpg", bbox_inches='tight')
50% of orders fall within a range of $ 5 - 14 ,the concentration of orders around the median and the decrease in frequency with increasing cost suggest that customers are price-sensitive. The mean is higher than the median, indicating that the tail of the distribution extends towards higher costs.
# Calculate the mean of the 'delivery_time' column
mean_delivery_time = data['delivery_time'].mean()
# Assuming 'data' contains your DataFrame and 'delivery_time' is a column in it
plt.figure(figsize=(7, 5))
plt.boxplot(data['delivery_time'], patch_artist=True, boxprops=dict(facecolor='lightblue'))
plt.axhline(y=mean_delivery_time, color='red', linestyle='--', label=f'Mean: {mean_delivery_time:.2f} min')
plt.title('Boxplot of Delivery Time')
plt.legend()
plt.show()
# Calculate the mean of the 'Delivery time' column
mean_cost = np.mean(data['delivery_time'])
print(f"Mean of delivery time: {mean_cost:.3f}")
plt.savefig("output.jpg", bbox_inches='tight')
# Calculate the mean of the 'food_preparation_time' column
mean_food_preparation_time = data['food_preparation_time'].mean()
# Boxplot for 'food_preparation_time'
plt.figure(figsize=(8, 6))
plt.boxplot(data['food_preparation_time'], patch_artist=True, boxprops=dict(facecolor='lightblue'))
plt.axhline(y=mean_food_preparation_time, color='red', linestyle='--', label=f'Mean: {mean_food_preparation_time:.2f} min')
plt.title('Boxplot of Food Preparation Time')
plt.legend()
plt.show()
# Calculate the mean of the 'Food Preparation time' column
mean_cost = np.mean(data['food_preparation_time'])
print(f"Mean of Food Preparation Time: {mean_cost:.3f}")
The mean and median are almost the same, it indicates that the data is approximately symmetrically distributed.
# Explore 'cuisine_type' variable with a countplot
plt.figure(figsize=(8, 6))
sns.countplot(x='cuisine_type', data=data)
plt.title('Count of Orders by Cuisine Type')
plt.xlabel('Cuisine Type')
plt.ylabel('Count')
plt.xticks(rotation=90) # Rotate x-axis labels for better readability
plt.show()
df = pd.DataFrame(data)
# Count the number of restaurants for each cuisine type
restaurant_count = df['cuisine_type'].value_counts()
print(restaurant_count)
American cuisine is the most popular, with 584 orders, accounting for 30.8% of the total. Japanese and Italian come in second and third with 470 (24.8%) and 298 (15.7%) orders respectively.
# Assuming 'df' is your DataFrame
# Visualize number of restaurants per cuisine type
plt.figure(figsize=(10, 6))
sns.barplot(x=restaurant_count_per_cuisine.index, y=restaurant_count_per_cuisine.values)
plt.title('Number of Restaurants per Cuisine Type')
plt.xlabel('Cuisine Type')
plt.ylabel('Count of Restaurants')
plt.xticks(rotation=45) # Rotate x-axis labels for better readability
plt.tight_layout()
plt.show()
# Count the number of unique restaurants
unique_restaurants = df['restaurant_name'].nunique()
print("Number of unique restaurants:", unique_restaurants)
# Count the number of restaurants for each cuisine type
restaurant_count_per_cuisine = df.groupby('cuisine_type')['restaurant_name'].nunique()
print("Number of restaurants per cuisine type:")
print(restaurant_count_per_cuisine)
A large number of restaurants have high demand, such as American, Japanese, and Italian. However, Korean, Thai, and Vietnamese restaurants have significantly fewer orders in comparison.
# Create a pie chart for orders on different days of the week
day_counts = data['day_of_the_week'].value_counts()
plt.figure(figsize=(6, 6))
plt.pie(day_counts, labels=day_counts.index, autopct='%1.1f%%')
plt.title('Orders on Different Days of the Week')
plt.show()
The influx of orders on weekends is considerably higher than on weekdays
# Creating a countplot for ratings
plt.figure(figsize=(8, 6))
sns.countplot(data=data, x='rating')
plt.title('Distribution of Ratings')
plt.xlabel('Rating')
plt.ylabel('Count')
plt.show()
Many customer avoid giving ratings altogether
# Write the code here
top_restaurants = df['restaurant_name'].value_counts().head(5),
print(top_restaurants)
plt.figure(figsize=(8, 6))
sns.countplot(data=data, x='restaurant_name', order=data['restaurant_name'].value_counts().head(5).index)
plt.title('Famous Restaurants')
plt.xlabel('Restaurant')
plt.ylabel('Count')
plt.xticks(rotation=90)
plt.show()
Shake Shack is far ahead, followed by The Meatball Shop and Blue Ribbon Sushi.
# Write the code here
weekend_cuisine = df[df['day_of_the_week'].isin(['Weekend'])]
popular_cuisine = weekend_cuisine['cuisine_type'].value_counts().idxmax()
print("The most popular cuisine on weekends is:", popular_cuisine)
# Count the occurrences of each cuisine type on weekends
weekend_cuisine = df[df['day_of_the_week'].isin(['Weekend'])]
popular_cuisine = weekend_cuisine['cuisine_type'].value_counts().idxmax()
cuisine_counts = weekend_cuisine['cuisine_type'].value_counts()
# Plotting
plt.figure(figsize=(10, 6))
cuisine_counts.plot(kind='bar', color='skyblue')
plt.title('Most Popular Cuisine on Weekends')
plt.xlabel('Cuisine Type')
plt.ylabel('Number of Orders')
plt.xticks(rotation=80)
plt.axhline(y=cuisine_counts.max(), color='red', linestyle='--', label=f'Most Popular Cuisine: {popular_cuisine}')
plt.legend()
plt.tight_layout()
plt.show()
American cuisine is the most popular, followed by Japanese cuisine
# Write the code here
# Count the number of orders with cost > 20
cost_above_20 = df[df['cost_of_the_order'] > 20]
num_orders_above_20 = len(cost_above_20)
# Total number of orders
total_orders = len(df)
# Calculate the percentage
percentage_above_20 = (num_orders_above_20 / total_orders) * 100
print(f"The percentage of orders costing more than 20 dollars is: {percentage_above_20:.2f}%")
Most orders are under $20. This finding underscores the importance of catering to budget-conscious customers.
import matplotlib.pyplot as plt
# Define labels and sizes for the pie chart
cost_above_20 = df[df['cost_of_the_order'] > 20]
num_orders_above_20 = len(cost_above_20)
labels = ['Orders > $20', 'Orders <= $20']
total_orders = len(df)
sizes = [num_orders_above_20, total_orders - num_orders_above_20]
colors = ['#ff9999', '#66b3ff'] # Define colors here
# Plotting the pie chart
plt.figure(figsize=(8, 6))
plt.pie(sizes, labels=labels, colors=colors, autopct='%1.1f%%', startangle=90)
plt.title('Percentage of Orders > $20')
plt.axis('equal') # Equal aspect ratio ensures that pie is drawn as a circle.
plt.show()
The majority of the orders fall below the $20 mark
# Write the code here
mean_delivery_time = df['delivery_time'].mean()
print(f"The mean order delivery time is: {mean_delivery_time:.2f} minutes")
# Calculate the mean of the 'delivery_time' column
mean_delivery_time = data['delivery_time'].mean()
# Assuming 'data' contains your DataFrame and 'delivery_time' is a column in it
plt.figure(figsize=(7, 5))
plt.boxplot(data['delivery_time'], patch_artist=True, boxprops=dict(facecolor='lightblue'))
plt.axhline(y=mean_delivery_time, color='red', linestyle='--', label=f'Mean: {mean_delivery_time:.2f} min')
plt.title('Boxplot of Delivery Time')
plt.legend()
plt.show()
# Calculate the mean of the 'Delivery time' column
mean_cost = np.mean(data['delivery_time'])
print(f"Mean of delivery time: {mean_cost:.3f}")
Majority of orders are delivered within a tight window of 20-27 minutes, ensuring consistent and efficient delivery.
# Write the code here
customer_order_counts = df['customer_id'].value_counts()
top_customers = customer_order_counts.head(3)
print("Top 3 customers and their order counts:")
print(top_customers)
Offering a 20% discount to these customers is a good way to reward their loyalty and encourage further purchases. It could also nudge them to try new dishes or increase their order size
sns.lineplot(data=df, x="cuisine_type" ,y = "delivery_time", hue="day_of_the_week", style="day_of_the_week", ci = False, markers = True) ;
plt.xticks(rotation=80)
Delivery times are generally faster on weekdays than on weekends. They also vary significantly between different cuisine types, even during weekends and weekdays. However, some cuisines are consistent in this regard, like Korean, Japanese, Mexican, American, and Italian.
# Filter the DataFrame for specific ratings
specific_ratings = ['1', '2', '3', '4', '5', 'Not given']
filtered_df = df[df['rating'].isin(specific_ratings)]
# Create the scatter plot
plt.figure(figsize=(8, 6))
sns.scatterplot(data=filtered_df, x='cost_of_the_order', y='rating', hue='rating')
plt.title('Scatter plot of Cost of the order vs Rating')
plt.xlabel('Cost of the Order')
plt.ylabel('Rating')
plt.legend(title='Rating')
plt.xticks(rotation=90) # Rotate x-axis labels for better visibility
plt.show()
Overall, not given ratings are consistent,There seems to be a weak positive correlation between cost of the order and rating.
g = sns.FacetGrid(df, col="cuisine_type")
g.map(sns.histplot, "cost_of_the_order");
Cost of the order and count of orders are directly propotionate, regardless of the cusine type.
g = sns.FacetGrid(df, col="cuisine_type")
g.map(sns.histplot, "delivery_time");
Delivery time of popular cuisine (American, Italian and Japanese) doesn't change with volume of the orders.
sns.boxplot(data=df, x='cuisine_type', y='cost_of_the_order')
plt.xticks(rotation=90)
Most of the cusine have median less than 15 dollars, with the exception of Thai and french cuisine.
sns.lmplot(data=df, x='food_preparation_time', y='delivery_time', col='rating');
Customer are pretty consistant in not given rating, regardless of the delivery being quick or delayed.
sns.boxplot(data=df, x='cuisine_type', y='delivery_time') ;
plt.xticks(rotation=90)
50% of Korean cuisine have enjoyed the quickest deliveries at the 20 minutes mark. Whereas most of the cusine are being delivered between 24 - 27 minutes
sns.jointplot(data=df, x='food_preparation_time', y='delivery_time', kind="hex");
plt.colorbar();
Approximately 89.5% of the order are delivered with in 60 minutes of placing an order by the customers.
# Write the code here
df = pd.DataFrame(data)
# Replace 'Not given' ratings with NaN
df['rating'] = df['rating'].replace('Not given', np.nan)
# Convert 'rating' column to numeric
df['rating'] = pd.to_numeric(df['rating'], errors='coerce')
# Calculate the rating count for each restaurant
restaurant_rating_count = df.groupby('restaurant_name')['rating'].count()
# Calculate the average rating for each restaurant
restaurant_avg_rating = df.groupby('restaurant_name')['rating'].mean()
# Filter restaurants based on conditions (including those with NaN ratings)
eligible_restaurants = (restaurant_rating_count > 50) & (restaurant_avg_rating > 4)
# Get the names of restaurants meeting the criteria
eligible_restaurant_names = eligible_restaurants[eligible_restaurants].index.tolist()
print("Restaurants eligible for the promotional offer:")
print(eligible_restaurant_names)
American, Japanese and Italian restaurants are eligible for the discounts as they are bringing more orders as well able to garner high rating.
# Write the code here
# Calculate revenue for orders based on specified criteria
revenue = 0
# Calculate revenue for orders costing more than $20 (25% charge)
revenue += data.loc[data['cost_of_the_order'] > 20, 'cost_of_the_order'].sum() * 0.25
# Calculate revenue for orders costing between $5 and $20 (15% charge)
revenue += data.loc[
(data['cost_of_the_order'] > 5) & (data['cost_of_the_order'] <= 20), 'cost_of_the_order'
].sum() * 0.15
print(f"Net revenue generated by the company: ${revenue:.2f}")
revenue_25_percent = data.loc[data['cost_of_the_order'] > 20, 'cost_of_the_order'].sum() * 0.25
revenue_15_percent = data.loc[
(data['cost_of_the_order'] > 5) & (data['cost_of_the_order'] <= 20), 'cost_of_the_order'
].sum() * 0.15
print(f"Net revenue generated by 25% charge orders: ${revenue_25_percent:.2f}")
print(f"Net revenue generated by 15% charge orders: ${revenue_15_percent:.2f}")
Company is earning 2477 dollars when the order is less than 20 dollars. On the other hand 3688 dollars is earning when the order are above 20 dollars.
# Write the code here
# Create a dataset
data["total_time"] = data["food_preparation_time"] + data["delivery_time"]
# Calculate the percentage of orders taking more than 60 minutes for delivery
total_orders = len(data)
more_than_60_minutes = len(data[data["total_time"] > 60])
percentage = (more_than_60_minutes / total_orders) * 100
print(f"Percentage of orders taking more than 60 minutes for delivery: {percentage:.2f}%")
After the placement of an order, 10.54% of the orders are taking more than 60 minutes to be delivered.
# Write the code here
# Assuming 'day_of_the_week' contains weekdays and weekends
weekday_mean_delivery_time = df[df['day_of_the_week'] == 'Weekday']['delivery_time'].mean()
weekend_mean_delivery_time = df[df['day_of_the_week'] == 'Weekend']['delivery_time'].mean()
print("Mean delivery time on weekdays:", weekday_mean_delivery_time)
print("Mean delivery time on weekends:", weekend_mean_delivery_time)
There is a 5 to 6 minutes difference between mean delivery time on weekends vs weekdays.