11.24每日总结
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 | import matplotlib as matplotlib import numpy as np import pandas as pd import seaborn as sns from pandas import DataFrame, Series # 可视化显示在界面 # matplotlib inline import matplotlib import matplotlib.pyplot as plt from wordcloud import STOPWORDS, WordCloud plt.rcParams[ 'font.sans-serif' ] = [ 'SimHei' ] # 用来显示中文 plt.rcParams[ 'axes.unicode_minus' ] = False # 用来正常显示负号 # 学习seaborn参考:https://www.jianshu.com/p/c26bc5ccf604 import json import warnings warnings.filterwarnings( 'ignore' ) # 设置显示的最大列、宽等参数,消掉打印不完全中间的省略号 # pd.set_option('display.max_columns', 1000) pd.set_option( 'display.width' , 1000 ) # 加了这一行那表格的一行就不会分段出现了 # pd.set_option('display.max_colwidth', 1000) # pd.set_option('display.height', 1000) # 显示所有列 pd.set_option( 'display.max_columns' , None ) # 显示所有行 pd.set_option( 'display.max_rows' , None ) movies = pd.read_csv( 'tmdb_5000_movies.csv' , encoding = 'utf_8' ) credits = pd.read_csv( 'tmdb_5000_credits.csv' , encoding = 'utf_8' ) movies.info() # 查看信息 credits.info() # 两个数据框都有title列,以及movies.riginal_title # 以上三个数据列重复,删除两个 del credits[ 'title' ] del movies[ 'original_title' ] # 连接两个csv文件 merged = pd.merge(movies, credits, left_on = 'id' , right_on = 'movie_id' , how = 'left' ) # 删除不需要分析的列 df = merged.drop([ 'homepage' , 'overview' , 'spoken_languages' , 'status' , 'tagline' , 'movie_id' ], axis = 1 ) df.info() # 查找缺失值记录-release_date var = df[df.release_date.isnull()] print (var.title) # 查找缺失值记录-runtime var = df[df.runtime.isnull()] print (var.title) df[ 'release_date' ] = df[ 'release_date' ].fillna( '2014-06-01' ) df.loc[ 2656 ] = df.loc[ 2656 ].fillna( '94, limit=1' ) df.loc[ 4140 ] = df.loc[ 4140 ].fillna( '240, limit=1' ) df.info() print ( len (df. id .unique())) df[ 'release_year' ] = pd.to_datetime(df.release_date, format = '%Y-%m-%d' , errors = 'coerce' ).dt.year df[ 'release_month' ] = pd.to_datetime(df.release_date). apply ( lambda x: x.month) df[ 'release_day' ] = pd.to_datetime(df.release_date). apply ( lambda x: x.day) df.info() print (df[ 'release_year' ], df[ 'release_month' ], df[ 'release_day' ]) df = df[(df.vote_count > = 50 ) & (df.budget * df.revenue * df.popularity * df.vote_average ! = 0 )].reset_index( drop = 'True' ) df.info() # Json格式处理 json_column = [ 'genres' , 'keywords' , 'production_companies' , 'production_countries' , 'cast' , 'crew' ] # 1-json本身为字符串类型,先转换为字典列表 for i in json_column: df[i] = df[i]. apply (json.loads) # 提取name # 2-将字典列表转换为以','分割的字符串 def get_name(x): return ',' .join([i[ 'name' ] for i in x]) df[ 'cast' ] = df[ 'cast' ]. apply (get_name) # 提取derector def get_director(x): for i in x: if i[ 'job' ] = = 'Director' : return i[ 'name' ] df[ 'crew' ] = df[ 'crew' ]. apply (get_director) for j in json_column[ 0 : 4 ]: df[j] = df[j]. apply (get_name) # 重命名 rename_dict = { 'cast' : 'actor' , 'crew' : 'director' } df.rename(columns = rename_dict, inplace = True ) df.info() print (df.head( 5 ).genres) print (df.head( 5 ).keywords) print (df.head( 5 ).production_companies) print (df.head( 5 ).production_countries) print (df.head( 5 ).actor) print (df.head( 5 ).director) # 数据备份 org_df = df.copy() df.reset_index().to_csv( "TMDB_5000_Movie_Dataset_Cleaned.csv" ) # 定义一个集合,获取所有的电影类型 genre = set () for i in df[ 'genres' ]. str .split( ',' ): # 去掉字符串之间的分隔符,得到单个电影类型 genre = set ().union(i, genre) # 集合求并集 # genre.update(i) #或者使用update方法 print (genre) # 将genre转变成列表 genre_list = list (genre) # 创建数据框-电影类型 genre_df = pd.DataFrame() # 对电影类型进行one-hot编码 for i in genre_list: # 如果包含类型 i,则编码为1,否则编码为0 genre_df[i] = df[ 'genres' ]. str .contains(i). apply ( lambda x: 1 if x else 0 ) # 将数据框的索引变为年份 genre_df.index = df[ 'release_year' ] # 计算得到每种类型的电影总数目,并降序排列 grnre_sum = genre_df. sum ().sort_values(ascending = False ) # 可视化 plt.rcParams[ 'font.sans-serif' ] = [ 'SimHei' ] # 用来显示中文 grnre_sum.plot(kind = 'bar' , label = 'genres' , figsize = ( 12 , 9 )) plt.title( '电影类型数量' , fontsize = 20 ) plt.xticks(rotation = 60 ) plt.xlabel( '类型' , fontsize = 16 ) plt.ylabel( '数量' , fontsize = 16 ) plt.grid( False ) plt.savefig( "电影类型数量-条形图.png" , dpi = 300 ) # 在 plt.show() 之前调用 plt.savefig() plt.show() # 绘制饼图 gen_shares = grnre_sum / grnre_sum. sum () # 设置other类,当电影类型所占比例小于%1时,全部归到other类中 others = 0.01 gen_pie = gen_shares[gen_shares > = others] gen_pie[ 'others' ] = gen_shares[gen_shares < others]. sum () # 设置分裂属性 # 所占比例小于或等于%2时,增大每块饼片边缘偏离半径的百分比 explode = (gen_pie < = 0.02 ) / 10 gen_pie.plot(kind = 'pie' , label = ' ', explode=explode, startangle=0, shadow=False, autopct=' % 3.1f % % ', figsize = ( 8 , 8 )) plt.title( '电影类型占比' , fontsize = 20 ) plt.savefig( "电影类型占比-饼图.png" , dpi = 300 ) plt.show() gen_year_sum = genre_df.sort_index(ascending = False ).groupby( 'release_year' ). sum () gen_year_sum_sub = gen_year_sum[ [ 'Drama' , 'Comedy' , 'Thriller' , 'Action' , 'Adventure' , 'Crime' , 'Romance' , 'Science Fiction' ]] gen_year_sum_sub.plot(figsize = ( 12 , 9 )) plt.legend(gen_year_sum_sub.columns) plt.xticks( range ( 1915 , 2018 , 10 )) plt.xlabel( '年份' , fontsize = 16 ) plt.ylabel( '数量' , fontsize = 16 ) plt.title( '电影类型变化趋势' , fontsize = 20 ) plt.grid( False ) plt.savefig( "电影类型变化趋势-折线图.png" , dpi = 600 ) plt.show() # Step1-创建profit_dataframe profit_df = pd.DataFrame() profit_df = pd.concat([genre_df.reset_index(), df[ 'revenue' ]], axis = 1 ) # Step2-创建profit_series,横坐标为genre profit_s = pd.Series(index = genre_list) # Step3-求出每种genre对应的利润均值 for i in genre_list: profit_s.loc[i] = profit_df.loc[:, [i, 'revenue' ]].groupby(i, as_index = False ).mean().loc[ 1 , 'revenue' ] profit_s = profit_s.sort_values(ascending = True ) # 计算不同类型电影的budget # Step1-创建profit_dataframe budget_df = pd.DataFrame() budget_df = pd.concat([genre_df.reset_index(), df[ 'budget' ]], axis = 1 ) # Step2-创建budget_series,横坐标为genre budget_s = pd.Series(index = genre_list) # Step3-求出每种genre对应的预算均值 for j in genre_list: budget_s.loc[j] = budget_df.loc[:, [j, 'budget' ]].groupby(j, as_index = False ).mean().loc[ 1 , 'budget' ] profit_budget = pd.concat([profit_s, budget_s], axis = 1 ) profit_budget.columns = [ 'revenue' , 'budget' ] profit_budget[ 'rate' ] = (profit_budget[ 'revenue' ] / profit_budget[ 'budget' ]) * 100 profit_budget_sort = profit_budget.sort_values(by = 'budget' , ascending = False ) # 绘制不同类型电影平均预算和利润率(组合图) x = profit_budget_sort.index y1 = profit_budget_sort.budget y2 = profit_budget_sort.rate # 返回profit_budget的行数 length = profit_budget_sort.shape[ 0 ] fig = plt.figure(figsize = ( 12 , 9 )) # 左轴 ax1 = fig.add_subplot( 1 , 1 , 1 ) plt.bar( range ( 0 , length), y1, color = 'b' , label = '平均预算' ) plt.xticks( range ( 0 , length), x, rotation = 90 , fontsize = 12 ) # 更改横坐标轴名称 ax1.set_xlabel( '年份' ) # 设置x轴label ,y轴label ax1.set_ylabel( '平均预算' , fontsize = 16 ) ax1.legend(loc = 2 , fontsize = 12 ) # 右轴 # 共享x轴,生成次坐标轴 ax2 = ax1.twinx() ax2.plot( range ( 0 , length), y2, 'ro-.' ) ax2.set_ylabel( '平均利润率' , fontsize = 16 ) ax2.legend(loc = 1 , fontsize = 12 ) # 将利润率坐标轴以百分比格式显示 import matplotlib.ticker as mtick fmt = '%.1f%%' yticks = mtick.FormatStrFormatter(fmt) ax2.yaxis.set_major_formatter(yticks) # 设置图片title ax1.set_title( '电影类型的平均预算和利润率' , fontsize = 20 ) ax1.grid( False ) ax2.grid( False ) plt.savefig( "电影类型的平均预算和利润率-组合图.png" , dpi = 300 ) plt.show() # 绘制不同类型电影预算和收入(条形图) profit_budget_sort.iloc[:, 0 : 2 ].plot(kind = 'bar' , figsize = ( 12 , 9 ), color = [ 'darkorange' , 'b' ]) plt.title( '平均预算(budget)与平均收入(revenue)' , fontsize = 20 ) plt.xlabel( 'len' , fontsize = 16 ) plt.grid( False ) plt.savefig( '电影类型的平均预算和平均收入-条形图.png' , dpi = 300 ) plt.show() # keywords关键词分析 keywords_list = [] for i in df[ 'keywords' ]: keywords_list.append(i) # print(keywords_list) # 把字符串列表连接成一个长字符串 lis = ''.join(keywords_list) lis.replace( '\'s' , '') # 设置停用词 stopwords = set (STOPWORDS) stopwords.add( 'film' ) stopwords.add( 'based' ) wordcloud = WordCloud( background_color = 'black' , random_state = 9 , # 设置一个随机种子,用于随机着色 stopwords = stopwords, max_words = 3000 , scale = 1 ).generate(lis) plt.figure(figsize = ( 10 , 6 )) plt.imshow(wordcloud) plt.axis( 'off' ) plt.savefig( '词云图.png' , dpi = 300 ) plt.show() print (df.runtime.head( 5 )) df.runtime = df.runtime.astype( float ) print (df.runtime.head( 5 )) sns.snsset_style( 'white' ) sns.distplot(df.runtime, bins = 20 ) sns.despine(left = True ) # 使用despine()方法来移除坐标轴,默认移除顶部和右侧坐标轴 plt.xticks( range ( 50 , 360 , 20 )) plt.savefig( '电影时长直方图.png' , dpi = 300 ) plt.show() fig = plt.figure(figsize = ( 8 , 6 )) x = list ( range ( 1 , 13 )) y1 = df.groupby( 'release_month' ).revenue.size() y2 = df.groupby( 'release_month' ).revenue.mean() # 每月单片平均票房 # 左轴 ax1 = fig.add_subplot( 1 , 1 , 1 ) plt.bar(x, y1, color = 'b' , label = '电影数量' ) plt.grid( False ) ax1.set_xlabel(u '月份' ) # 设置x轴label ,y轴label ax1.set_ylabel(u '每月电影数量' , fontsize = 16 ) ax1.legend(loc = 2 , fontsize = 12 ) # 右轴 ax2 = ax1.twinx() plt.plot(x, y2, 'ro--' , label = u '单片平均票房' ) ax2.set_ylabel(u '每月单片平均票房' , fontsize = 16 ) ax2.legend(loc = 1 , fontsize = 12 ) plt.rcParams[ 'font.sans-serif' ] = [ 'SimHei' ] plt.savefig( '每月电影数量和单片平均票房.png' , dpi = 300 ) plt.rc( "font" , family = "SimHei" , size = "15" ) plt.show() #票房分布及票房Top10的导演 # 创建数据框 - 导演 director_df = pd.DataFrame() director_df = df[[ 'director' , 'revenue' , 'budget' , 'vote_average' ]] director_df[ 'profit' ] = (director_df[ 'revenue' ] - director_df[ 'budget' ]) director_df = director_df.groupby(by = 'director' ).mean().sort_values(by = 'revenue' ,ascending = False ) # 取均值 director_df.info() # 绘制票房分布直方图 director_df[ 'revenue' ].plot.hist(bins = 100 , figsize = ( 8 , 6 )) plt.xlabel( '票房' ) plt.ylabel( '频数' ) plt.title( '导演的票房分布直方图' ) plt.savefig( '导演的票房分布直方图.png' ,dpi = 300 ) plt.show() # 票房均值Top10的导演 director_df.revenue.sort_values(ascending = True ).tail( 10 ).plot(kind = 'barh' ,figsize = ( 8 , 6 )) plt.xlabel( '票房' ,fontsize = 16 ) plt.ylabel( '导演' ,fontsize = 16 ) plt.title( '票房排名Top10的导演' ,fontsize = 20 ) plt.savefig( '票房排名Top10的导演.png' ,dpi = 300 ) plt.show() #评分分布及评分Top10的导演 # 绘制导演评分直方图 director_df[ 'vote_average' ].plot.hist(bins = 18 , figsize = ( 8 , 6 )) plt.xlabel( '评分' ) plt.ylabel( '频数' ) plt.title( '导演的评分分布直方图' ) plt.savefig( '导演的评分分布直方图.png' ,dpi = 300 ) plt.show() # 评分均值Top10的导演 director_df.vote_average.sort_values(ascending = True ).tail( 10 ).plot(kind = 'barh' ,figsize = ( 8 , 6 )) plt.xlabel( '评分' ,fontsize = 16 ) plt.ylabel( '导演' ,fontsize = 16 ) plt.title( '评分排名Top10的导演' ,fontsize = 20 ) plt.savefig( '评分排名Top10的导演.png' ,dpi = 300 ) plt.show() #原创 VS 改编占比(饼图) # 创建数据框 original_df = pd.DataFrame() original_df[ 'keywords' ] = df[ 'keywords' ]. str .contains( 'based on' ). map ( lambda x: 1 if x else 0 ) original_df[ 'profit' ] = df[ 'revenue' ] - df[ 'budget' ] original_df[ 'budget' ] = df[ 'budget' ] # 计算 novel_cnt = original_df[ 'keywords' ]. sum () # 改编作品数量 original_cnt = original_df[ 'keywords' ].count() - original_df[ 'keywords' ]. sum () # 原创作品数量 # 按照 是否原创 分组 original_df = original_df.groupby( 'keywords' , as_index = False ).mean() # 注意此处计算的是利润和预算的平均值 # 增加计数列 original_df[ 'count' ] = [original_cnt, novel_cnt] # 计算利润率 original_df[ 'profit_rate' ] = (original_df[ 'profit' ] / original_df[ 'budget' ]) * 100 # 修改index original_df.index = [ 'original' , 'based_on_novel' ] # 计算百分比 original_pie = original_df[ 'count' ] / original_df[ 'count' ]. sum () # 绘制饼图 original_pie.plot(kind = 'pie' ,label = ' ',startangle=90,shadow=False,autopct=' % 2.1f % % ',figsize = ( 8 , 8 )) plt.title( '改编 VS 原创' ,fontsize = 20 ) plt.legend(loc = 2 ,fontsize = 10 ) plt.savefig( '改编VS原创-饼图.png' ,dpi = 300 ) plt.show() #原创VS改编 预算/利润率(组合图) x = original_df.index y1 = original_df.budget y2 = original_df.profit_rate fig = plt.figure(figsize = ( 8 , 6 )) # 左轴 ax1 = fig.add_subplot( 1 , 1 , 1 ) plt.bar(x,y1,color = 'b' ,label = '平均预算' ,width = 0.25 ) plt.xticks(rotation = 0 , fontsize = 12 ) # 更改横坐标轴名称 ax1.set_xlabel( '原创 VS 改编' ) # 设置x轴label ,y轴label ax1.set_ylabel( '平均预算' ,fontsize = 16 ) ax1.legend(loc = 2 ,fontsize = 10 ) #右轴 # 共享x轴,生成次坐标轴 ax2 = ax1.twinx() ax2.plot(x,y2,color = 'r' ,label = '平均利润率' ) ax2.set_ylabel( '平均利润率' ,fontsize = 16 ) ax2.legend(loc = 1 ,fontsize = 10 ) # loc=1,2,3,4分别表示四个角,和四象限顺序一致 # 将利润率坐标轴以百分比格式显示 import matplotlib.ticker as mtick fmt = '%.1f%%' yticks = mtick.FormatStrFormatter(fmt) ax2.yaxis.set_major_formatter(yticks) plt.savefig( '改编VS原创的预算以及利润率-组合图.png' ,dpi = 300 ) plt.show() revenue_corr = df[[ 'runtime' , 'popularity' , 'vote_average' , 'vote_count' , 'budget' , 'revenue' ]].corr() sns.heatmap( revenue_corr, annot = True , # 在每个单元格内显示标注 cmap = "Blues" , # 设置填充颜色:黄色,绿色,蓝色 cbar = True , # 显示color bar linewidths = 0.5 , # 在单元格之间加入小间隔,方便数据阅读 ) plt.savefig( '票房相关系数矩阵.png' ,dpi = 300 ) plt.show() fig = plt.figure(figsize = ( 17 , 5 )) ax1 = plt.subplot( 1 , 3 , 1 ) ax1 = sns.regplot(x = 'budget' , y = 'revenue' , data = df, x_jitter = . 1 ,color = 'r' ,marker = 'x' ) # marker: 'x','o','v','^','<' # jitter:抖动项,表示抖动程度 ax1.text( 1.6e8 , 2.2e9 , 'r=0.7' ,fontsize = 16 ) plt.title( 'budget-revenue-scatter' ,fontsize = 20 ) plt.xlabel( 'budget' ,fontsize = 16 ) plt.ylabel( 'revenue' ,fontsize = 16 ) ax2 = plt.subplot( 1 , 3 , 2 ) ax2 = sns.regplot(x = 'popularity' , y = 'revenue' , data = df, x_jitter = . 1 ,color = 'g' ,marker = 'o' ) ax2.text( 500 , 3e9 , 'r=0.59' ,fontsize = 16 ) plt.title( 'popularity-revenue-scatter' ,fontsize = 18 ) plt.xlabel( 'popularity' ,fontsize = 16 ) plt.ylabel( 'revenue' ,fontsize = 16 ) ax3 = plt.subplot( 1 , 3 , 3 ) ax3 = sns.regplot(x = 'vote_count' , y = 'revenue' , data = df, x_jitter = . 1 ,color = 'b' ,marker = 'v' ) ax3.text( 7000 , 2e9 , 'r=0.75' ,fontsize = 16 ) plt.title( 'voteCount-revenue-scatter' ,fontsize = 20 ) plt.xlabel( 'vote_count' ,fontsize = 16 ) plt.ylabel( 'revenue' ,fontsize = 16 ) plt.savefig( 'revenue.png' ,dpi = 300 ) plt.show() |
今天进行了大数据测试
练习题:观影大数据分析
王 S 聪想要在海外开拓万 D 电影的市场,这次他在考虑:怎么拍商业电影才能赚钱?毕竟一些制作成本超过 1 亿美元的大型电影也会失败。这个问题对电影业来说比以往任何时候都更加重要。 所以,他就请来了你(数据分析师)来帮他解决问题,给出一些建议,根据数据分析一下商业电影的成功是否存在统一公式?以帮助他更好地进行决策。
解决的终极问题是:电影票房的影响因素有哪些?接下来我们就分不同的维度分析: • 观众喜欢什么电影类型?有什么主题关键词? • 电影风格随时间是如何变化的? • 电影预算高低是否影响票房? • 高票房或者高评分的导演有哪些? • 电影的发行时间最好选在啥时候? • 拍原创电影好还是改编电影好?本次使用的数据来自于 Kaggle 平台(TMDb 5000 Movie Database)。收录了美国地区 1916-2017 年近 5000 部电影的数据,包含预算、导演、票房、电影评分等信息。原始数据集包含 2 个文件:
• tmdb_5000_movies:电影基本信息,包含 20 个变量 • tmdb_5000_credits:演职员信息,包含 4 个变量
请使用 Python 编程,完成下列问题:
(1) 使用附件中的 tmdb_5000_movies.csv 和 tmdb_5000_credits.csv 数据集,进行数据清洗、数据挖掘、数据分析和数据可视化等,研究电影票房的影响因素有哪些?从不同的维度分析电影,讨论并分析你的结果。
(2) 附件 tmdb_1000_predict.csv 中包含 1000 部电影的基本信息,请你选择合适的指标,进行特征提取,建立机器学习的预测模型,预测 1000 部电影的 vote_average 和 vote_count,并保存为 tmdb_1000_predicted.csv。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix