十二月 21, 2025
31 分钟阅读

初级数据科学家面试题:Python、SQL、统计与机器学习

interview
career-advice
job-search
entry-level
初级数据科学家面试题:Python、SQL、统计与机器学习
Milad Bonakdar

Milad Bonakdar

作者

用 Python、pandas、SQL、统计学、机器学习基础和项目讲解问题,系统准备初级数据科学家面试。


简介

数据科学结合了统计学、编程和领域知识,以从数据中提取见解。初级数据科学家应具备扎实的 Python、统计学、机器学习基础和数据处理工具方面的基础。

本指南涵盖了初级数据科学家必备的面试问题。我们将探讨 Python 编程、统计学基础、使用 pandas 进行数据处理、机器学习概念、数据可视化和 SQL,以帮助你为你的第一个数据科学职位做好准备。


Python 基础 (5 个问题)

1. Python 中的列表 (list) 和元组 (tuple) 有什么区别?

回答:

  • 列表 (List): 可变的 (可以修改),用方括号 [] 定义
  • 元组 (Tuple): 不可变的 (不能修改),用圆括号 () 定义
  • 性能: 元组稍微快一些,并且占用更少的内存
  • 用例:
    • 列表:当你需要修改数据时
    • 元组:用于固定集合、字典键、函数返回值
# 列表 - 可变
my_list = [1, 2, 3]
my_list[0] = 10  # 有效
my_list.append(4)  # 有效
print(my_list)  # [10, 2, 3, 4]

# 元组 - 不可变
my_tuple = (1, 2, 3)
# my_tuple[0] = 10  # 错误:元组是不可变的
# my_tuple.append(4)  # 错误:没有 append 方法

# 元组解包
x, y, z = (1, 2, 3)
print(x, y, z)  # 1 2 3

稀有度: 非常常见 难度: 简单


2. 解释列表推导式 (list comprehension) 并给出一个例子。

回答: 列表推导式提供了一种基于现有可迭代对象创建列表的简洁方法。

  • 语法: [expression for item in iterable if condition]
  • 优点: 更具可读性,通常比循环更快
# 传统循环
squares = []
for i in range(10):
    squares.append(i ** 2)

# 列表推导式
squares = [i ** 2 for i in range(10)]
print(squares)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# 带条件
even_squares = [i ** 2 for i in range(10) if i % 2 == 0]
print(even_squares)  # [0, 4, 16, 36, 64]

# 嵌套推导式
matrix = [[i * j for j in range(3)] for i in range(3)]
print(matrix)  # [[0, 0, 0], [0, 1, 2], [0, 2, 4]]

# 字典推导式
squares_dict = {i: i ** 2 for i in range(5)}
print(squares_dict)  # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

稀有度: 非常常见 难度: 简单


3. 什么是 lambda 函数,你会在什么时候使用它们?

回答: Lambda 函数是匿名的、单表达式的函数。

  • 语法: lambda arguments: expression
  • 用例: 短函数、回调、排序、过滤
# 常规函数
def square(x):
    return x ** 2

# Lambda 函数
square_lambda = lambda x: x ** 2
print(square_lambda(5))  # 25

# 与 map 一起使用
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x ** 2, numbers))
print(squared)  # [1, 4, 9, 16, 25]

# 与 filter 一起使用
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens)  # [2, 4]

# 使用 key 进行排序
students = [('Alice', 85), ('Bob', 92), ('Charlie', 78)]
sorted_students = sorted(students, key=lambda x: x[1], reverse=True)
print(sorted_students)  # [('Bob', 92), ('Alice', 85), ('Charlie', 78)]

稀有度: 非常常见 难度: 简单


4. 解释列表的 append()extend() 方法之间的区别。

回答:

  • append(): 将单个元素添加到列表的末尾
  • extend(): 将来自可迭代对象的多个元素添加到列表的末尾
# append - 添加单个元素
list1 = [1, 2, 3]
list1.append(4)
print(list1)  # [1, 2, 3, 4]

list1.append([5, 6])
print(list1)  # [1, 2, 3, 4, [5, 6]] - 列表作为单个元素

# extend - 添加多个元素
list2 = [1, 2, 3]
list2.extend([4, 5, 6])
print(list2)  # [1, 2, 3, 4, 5, 6]

# 替代 extend 的方法
list3 = [1, 2, 3]
list3 += [4, 5, 6]
print(list3)  # [1, 2, 3, 4, 5, 6]

稀有度: 常见 难度: 简单


5. 什么是 *args**kwargs

回答: 它们允许函数接受可变数量的参数。

  • *args 可变数量的位置参数 (元组)
  • **kwargs 可变数量的关键字参数 (字典)
# *args - 位置参数
def sum_all(*args):
    return sum(args)

print(sum_all(1, 2, 3))  # 6
print(sum_all(1, 2, 3, 4, 5))  # 15

# **kwargs - 关键字参数
def print_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

print_info(name="Alice", age=25, city="NYC")
# name: Alice
# age: 25
# city: NYC

# 组合使用
def flexible_function(*args, **kwargs):
    print("Positional:", args)
    print("Keyword:", kwargs)

flexible_function(1, 2, 3, name="Alice", age=25)
# Positional: (1, 2, 3)
# Keyword: {'name': 'Alice', 'age': 25}

稀有度: 常见 难度: 中等


统计学与概率 (5 个问题)

6. 均值 (mean)、中位数 (median) 和众数 (mode) 之间有什么区别?

回答:

  • 均值 (Mean): 所有值的平均值 (总和 / 计数)
  • 中位数 (Median): 排序后的中间值
  • 众数 (Mode): 最常出现的值
  • 何时使用:
    • 均值:正态分布的数据
    • 中位数:偏斜数据或存在异常值
    • 众数:分类数据
import numpy as np
from scipy import stats

data = [1, 2, 2, 3, 4, 5, 100]

# 均值 - 受异常值影响
mean = np.mean(data)
print(f"Mean: {mean}")  # 16.71

# 中位数 - 对异常值具有鲁棒性
median = np.median(data)
print(f"Median: {median}")  # 3

# 众数
mode = stats.mode(data, keepdims=True)
print(f"Mode: {mode.mode[0]}")  # 2

稀有度: 非常常见 难度: 简单


7. 解释方差 (variance) 和标准差 (standard deviation)。

回答:

  • 方差 (Variance): 与均值的平均平方偏差
  • 标准差 (Standard Deviation): 方差的平方根 (与数据相同的单位)
  • 目的: 衡量数据的离散程度/分散程度
import numpy as np

data = [2, 4, 4, 4, 5, 5, 7, 9]

# 方差
variance = np.var(data, ddof=1)  # ddof=1 用于样本方差
print(f"Variance: {variance}")  # 4.57

# 标准差
std_dev = np.std(data, ddof=1)
print(f"Std Dev: {std_dev}")  # 2.14

# 手动计算
mean = np.mean(data)
variance_manual = sum((x - mean) ** 2 for x in data) / (len(data) - 1)
print(f"Manual Variance: {variance_manual}")

稀有度: 非常常见 难度: 简单


8. 什么是 p 值,你如何解释它?

回答: p 值是在假设零假设为真的情况下,获得至少与观察到的结果一样极端的结果的概率。

  • 解释:
    • p < 0.05: 拒绝零假设 (具有统计显著性)
    • p ≥ 0.05: 无法拒绝零假设
  • 注意: p 值不衡量效应量或重要性
from scipy import stats

# 示例:测试硬币是否公平
# 零假设:硬币是公平的 (p = 0.5)
# 我们在 100 次抛掷中得到 65 次正面

observed_heads = 65
n_flips = 100
expected_proportion = 0.5

# 二项检验
p_value = stats.binom_test(observed_heads, n_flips, expected_proportion)
print(f"P-value: {p_value}")  # 0.0018

if p_value < 0.05:
    print("Reject null hypothesis - coin is likely biased")
else:
    print("Fail to reject null hypothesis - coin appears fair")

稀有度: 非常常见 难度: 中等


9. 什么是中心极限定理?

回答: 中心极限定理指出,随着样本量的增加,样本均值的抽样分布趋近于正态分布,而与总体的分布无关。

  • 要点:
    • 适用于任何分布 (如果样本量足够大)
    • 通常认为 n ≥ 30 就足够了
    • 能够进行假设检验和置信区间估计
import numpy as np
import matplotlib.pyplot as plt

# 具有非正态分布的总体 (指数分布)
population = np.random.exponential(scale=2, size=10000)

# 抽取多个样本并计算它们的均值
sample_means = []
for _ in range(1000):
    sample = np.random.choice(population, size=30)
    sample_means.append(np.mean(sample))

# 样本均值呈正态分布 (CLT)
print(f"Population mean: {np.mean(population):.2f}")
print(f"Mean of sample means: {np.mean(sample_means):.2f}")
print(f"Std of sample means: {np.std(sample_means):.2f}")

稀有度: 常见 难度: 中等


10. 什么是相关性 (correlation) 与因果关系 (causation)?

回答:

  • 相关性 (Correlation): 两个变量之间的统计关系
  • 因果关系 (Causation): 一个变量直接导致另一个变量发生变化
  • 要点: 相关性并不意味着因果关系
  • 原因:
    • 混淆变量
    • 反向因果关系
    • 巧合
import numpy as np
import pandas as pd

# 示例:冰淇淋销量与溺水死亡人数相关
# 但冰淇淋不会导致溺水 (混淆变量:温度)

# 相关系数
x = np.array([1, 2, 3, 4, 5])
y = np.array([2, 4, 5, 4, 5])

correlation = np.corrcoef(x, y)[0, 1]
print(f"Correlation: {correlation:.2f}")  # 0.82

# Pearson 相关系数
from scipy.stats import pearsonr
corr, p_value = pearsonr(x, y)
print(f"Pearson r: {corr:.2f}, p-value: {p_value:.3f}")

稀有度: 非常常见 难度: 简单


使用 Pandas 进行数据处理 (5 个问题)

11. 如何读取 CSV 文件并显示基本信息?

回答: 使用 pandas 读取和探索数据。

import pandas as pd

# 读取 CSV
df = pd.read_csv('data.csv')

# 基本信息
print(df.head())  # 前 5 行
print(df.tail())  # 后 5 行
print(df.shape)   # (行数, 列数)
print(df.info())  # 数据类型和非空计数
print(df.describe())  # 统计摘要

# 列名和类型
print(df.columns)
print(df.dtypes)

# 检查缺失值
print(df.isnull().sum())

# 特定列
print(df[['column1', 'column2']].head())

稀有度: 非常常见 难度: 简单


12. 如何处理 DataFrame 中的缺失值?

回答: 处理缺失数据的多种策略:

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'A': [1, 2, np.nan, 4],
    'B': [5, np.nan, np.nan, 8],
    'C': [9, 10, 11, 12]
})

# 检查缺失值
print(df.isnull().sum())

# 删除包含任何缺失值的行
df_dropped = df.dropna()

# 删除包含任何缺失值的列
df_dropped_cols = df.dropna(axis=1)

# 用特定值填充
df_filled = df.fillna(0)

# 用均值填充
df['A'] = df['A'].fillna(df['A'].mean())

# 用中位数填充
df['B'] = df['B'].fillna(df['B'].median())

# 前向填充 (使用前一个值)
df_ffill = df.fillna(method='ffill')

# 后向填充 (使用下一个值)
df_bfill = df.fillna(method='bfill')

# 插值
df_interpolated = df.interpolate()

稀有度: 非常常见 难度: 简单


13. 如何在 pandas 中过滤和选择数据?

回答: 过滤和选择数据的多种方法:

import pandas as pd

df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Charlie', 'David'],
    'age': [25, 30, 35, 28],
    'salary': [50000, 60000, 75000, 55000],
    'department': ['IT', 'HR', 'IT', 'Finance']
})

# 选择列
print(df['name'])  # 单列 (Series)
print(df[['name', 'age']])  # 多列 (DataFrame)

# 过滤行
high_salary = df[df['salary'] > 55000]
print(high_salary)

# 多个条件
it_high_salary = df[(df['department'] == 'IT') & (df['salary'] > 50000)]
print(it_high_salary)

# 使用 .loc (基于标签)
print(df.loc[0:2, ['name', 'age']])

# 使用 .iloc (基于位置)
print(df.iloc[0:2, 0:2])

# Query 方法
result = df.query('age > 28 and salary > 55000')
print(result)

# isin 方法
it_or_hr = df[df['department'].isin(['IT', 'HR'])]
print(it_or_hr)

稀有度: 非常常见 难度: 简单


14. 如何分组和聚合数据?

回答: 使用 groupby() 进行聚合操作:

import pandas as pd

df = pd.DataFrame({
    'department': ['IT', 'HR', 'IT', 'Finance', 'HR', 'IT'],
    'employee': ['Alice', 'Bob', 'Charlie', 'David', 'Eve', 'Frank'],
    'salary': [50000, 45000, 60000, 55000, 48000, 65000],
    'age': [25, 30, 35, 28, 32, 40]
})

# 按单列分组
dept_avg_salary = df.groupby('department')['salary'].mean()
print(dept_avg_salary)

# 多个聚合
dept_stats = df.groupby('department').agg({
    'salary': ['mean', 'min', 'max'],
    'age': 'mean'
})
print(dept_stats)

# 自定义聚合
dept_custom = df.groupby('department').agg({
    'salary': lambda x: x.max() - x.min(),
    'employee': 'count'
})
print(dept_custom)

# 按多个列分组
result = df.groupby(['department', 'age'])['salary'].sum()
print(result)

稀有度: 非常常见 难度: 中等


15. 如何合并或连接 DataFrames?

回答: 使用 merge()join()concat()

import pandas as pd

# 示例 DataFrames
df1 = pd.DataFrame({
    'employee_id': [1, 2, 3, 4],
    'name': ['Alice', 'Bob', 'Charlie', 'David']
})

df2 = pd.DataFrame({
    'employee_id': [1, 2, 3, 5],
    'salary': [50000, 60000, 75000, 55000]
})

# 内连接 (仅匹配的行)
inner = pd.merge(df1, df2, on='employee_id', how='inner')
print(inner)

# 左连接 (来自左侧的所有行)
left = pd.merge(df1, df2, on='employee_id', how='left')
print(left)

# 右连接 (来自右侧的所有行)
right = pd.merge(df1, df2, on='employee_id', how='right')
print(right)

# 外连接 (来自两侧的所有行)
outer = pd.merge(df1, df2, on='employee_id', how='outer')
print(outer)

# 垂直连接
df3 = pd.concat([df1, df2], ignore_index=True)
print(df3)

# 水平连接
df4 = pd.concat([df1, df2], axis=1)
print(df4)

稀有度: 非常常见 难度: 中等


机器学习基础 (5 个问题)

16. 监督学习和无监督学习有什么区别?

回答:

  • 监督学习:
    • 具有标记的训练数据 (输入-输出对)
    • 目标:学习从输入到输出的映射
    • 示例:分类、回归
    • 算法:线性回归、决策树、SVM
  • 无监督学习:
    • 没有标记的数据 (只有输入)
    • 目标:在数据中找到模式或结构
    • 示例:聚类、降维
    • 算法:K-Means、PCA、层次聚类
from sklearn.linear_model import LinearRegression
from sklearn.cluster import KMeans
import numpy as np

# 监督学习 - 线性回归
X_train = np.array([[1], [2], [3], [4], [5]])
y_train = np.array([2, 4, 6, 8, 10])

model = LinearRegression()
model.fit(X_train, y_train)
prediction = model.predict([[6]])
print(f"Supervised prediction: {prediction[0]}")  # 12

# 无监督学习 - K-Means 聚类
X = np.array([[1, 2], [1.5, 1.8], [5, 8], [8, 8], [1, 0.6], [9, 11]])

kmeans = KMeans(n_clusters=2, random_state=42)
clusters = kmeans.fit_predict(X)
print(f"Cluster assignments: {clusters}")

稀有度: 非常常见 难度: 简单


17. 什么是过拟合 (overfitting),你如何防止它?

回答: 当模型学习训练数据过于深入,包括噪声,并且在新数据上表现不佳时,就会发生过拟合。

  • 迹象:
    • 高训练准确率,低测试准确率
    • 模型对于数据来说过于复杂
  • 预防:
    • 更多训练数据
    • 交叉验证
    • 正则化 (L1, L2)
    • 更简单的模型
    • 提前停止
    • Dropout (神经网络)
from sklearn.model_selection import train_test_split
from sklearn.linear_model import Ridge, Lasso
from sklearn.preprocessing import PolynomialFeatures
import numpy as np

# 生成数据
X = np.random.rand(100, 1) * 10
y = 2 * X + 3 + np.random.randn(100, 1) * 2

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

# 过拟合示例 - 高阶多项式
poly = PolynomialFeatures(degree=15)
X_poly = poly.fit_transform(X_train)

# 正则化以防止过拟合
# Ridge (L2 正则化)
ridge = Ridge(alpha=1.0)
ridge.fit(X_poly, y_train)

# Lasso (L1 正则化)
lasso = Lasso(alpha=0.1)
lasso.fit(X_poly, y_train)

print(f"Ridge score: {ridge.score(X_poly, y_train)}")
print(f"Lasso score: {lasso.score(X_poly, y_train)}")

稀有度: 非常常见 难度: 中等


18. 解释训练-测试集分割 (train-test split) 及其重要性。

回答: 训练-测试集分割将数据划分为训练集和测试集,以评估模型在未见过的数据上的性能。

  • 目的: 防止过拟合,估计真实世界的性能
  • 典型分割: 70-30 或 80-20 (训练-测试)
  • 交叉验证: 更稳健的评估
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_iris

# 加载数据
iris = load_iris()
X, y = iris.data, iris.target

# 训练-测试集分割
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42
)

print(f"Training set size: {len(X_train)}")
print(f"Test set size: {len(X_test)}")

# 训练模型
model = LogisticRegression(max_iter=200)
model.fit(X_train, y_train)

# 评估
train_score = model.score(X_train, y_train)
test_score = model.score(X_test, y_test)

print(f"Training accuracy: {train_score:.2f}")
print(f"Test accuracy: {test_score:.2f}")

# 交叉验证 (更稳健)
cv_scores = cross_val_score(model, X, y, cv=5)
print(f"CV scores: {cv_scores}")
print(f"Mean CV score: {cv_scores.mean():.2f}")

稀有度: 非常常见 难度: 简单


19. 你使用哪些评估指标来衡量分类模型的性能?

回答: 不同的场景使用不同的指标:

  • 准确率 (Accuracy): 总体正确率 (适用于平衡数据集)
  • 精确率 (Precision): 在预测为正例的样本中,有多少是正确的
  • 召回率 (Recall): 在实际为正例的样本中,有多少被找到了
  • F1-Score: 精确率和召回率的调和平均值
  • 混淆矩阵 (Confusion Matrix): 预测结果的详细分解
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
from sklearn.metrics import confusion_matrix, classification_report
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer

# 加载数据
data = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(
    data.data, data.target, test_size=0.3, random_state=42
)

# 训练模型
model = LogisticRegression(max_iter=10000)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)

# 指标
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)

print(f"Accuracy: {accuracy:.2f}")
print(f"Precision: {precision:.2f}")
print(f"Recall: {recall:.2f}")
print(f"F1-Score: {f1:.2f}")

# 混淆矩阵
cm = confusion_matrix(y_test, y_pred)
print(f"\nConfusion Matrix:\n{cm}")

# 分类报告
print(f"\n{classification_report(y_test, y_pred)}")

稀有度: 非常常见 难度: 中等


20. 分类和回归有什么区别?

回答:

  • 分类:
    • 预测离散的类别/类
    • 输出:类标签
    • 示例:垃圾邮件检测、图像分类
    • 算法:逻辑回归、决策树、SVM
    • 指标:准确率、精确率、召回率、F1
  • 回归:
    • 预测连续的数值
    • 输出:数字
    • 示例:房价预测、温度预测
    • 算法:线性回归、随机森林回归
    • 指标:MSE、RMSE、MAE、R²
from sklearn.linear_model import LinearRegression, LogisticRegression
from sklearn.metrics import mean_squared_error, r2_score
import numpy as np

# 回归示例
X_reg = np.array([[1], [2], [3], [4], [5]])
y_reg = np.array([2.1, 3.9, 6.2, 7.8, 10.1])

reg_model = LinearRegression()
reg_model.fit(X_reg, y_reg)
y_pred_reg = reg_model.predict([[6]])
print(f"Regression prediction: {y_pred_reg[0]:.2f}")  # 连续值

# 分类示例
X_clf = np.array([[1], [2], [3], [4], [5]])
y_clf = np.array([0, 0, 1, 1, 1])  # 二元类

clf_model = LogisticRegression()
clf_model.fit(X_clf, y_clf)
y_pred_clf = clf_model.predict([[3.5]])
print(f"Classification prediction: {y_pred_clf[0]}")  # 类标签 (0 或 1)

稀有度: 非常常见 难度: 简单

Newsletter subscription

真正有效的每周职业建议

将最新见解直接发送到您的收件箱

在招聘人员面前脱颖而出,获得梦想工作

加入成千上万通过AI驱动的简历改变职业生涯的人,这些简历可以通过ATS并给招聘经理留下深刻印象。

立即开始创建

分享这篇文章

让面试回访翻倍

根据职位描述定制简历的候选人获得的面试机会是其他人的2.5倍。使用我们的AI为每一份申请即时自动定制您的简历。